summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-14 13:23:16 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-14 13:23:16 +0000
commit3e97c51418e6d27e9a81906f347fcb7c78e27d4f (patch)
treeee596ce1bc9840661386f96f9b8d1f919a106317
parentInitial commit. (diff)
downloadicingaweb2-module-incubator-3e97c51418e6d27e9a81906f347fcb7c78e27d4f.tar.xz
icingaweb2-module-incubator-3e97c51418e6d27e9a81906f347fcb7c78e27d4f.zip
Adding upstream version 0.20.0.upstream/0.20.0
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
-rw-r--r--.gitignore6
-rw-r--r--LICENSE21
-rw-r--r--README.md76
-rwxr-xr-xbin/make-release.sh51
-rwxr-xr-xbin/refresh-public.sh3
-rw-r--r--composer.json52
-rw-r--r--composer.lock1283
-rw-r--r--configuration.php4
-rw-r--r--extendStyles.php21
-rw-r--r--module.info6
-rw-r--r--public/css/combined.less969
-rw-r--r--public/js/combined.js69
-rw-r--r--public/js/loader.js54
-rw-r--r--public/js/module.js19
-rw-r--r--run.php14
-rw-r--r--vendor/autoload.php7
-rw-r--r--vendor/composer/ClassLoader.php572
-rw-r--r--vendor/composer/InstalledVersions.php337
-rw-r--r--vendor/composer/LICENSE21
-rw-r--r--vendor/composer/autoload_classmap.php10
-rw-r--r--vendor/composer/autoload_namespaces.php9
-rw-r--r--vendor/composer/autoload_psr4.php38
-rw-r--r--vendor/composer/autoload_real.php57
-rw-r--r--vendor/composer/autoload_static.php179
-rw-r--r--vendor/composer/installed.json1350
-rw-r--r--vendor/composer/installed.php524
-rw-r--r--vendor/composer/platform_check.php26
-rw-r--r--vendor/gipfl/calendar/composer.json32
-rw-r--r--vendor/gipfl/calendar/src/Calendar.php246
-rw-r--r--vendor/gipfl/calendar/src/Widget/CalendarMonth.php177
-rw-r--r--vendor/gipfl/calendar/src/Widget/CalendarMonthSummary.php278
-rw-r--r--vendor/gipfl/cli/composer.json31
-rw-r--r--vendor/gipfl/cli/src/AnsiScreen.php128
-rw-r--r--vendor/gipfl/cli/src/Process.php141
-rw-r--r--vendor/gipfl/cli/src/Screen.php190
-rw-r--r--vendor/gipfl/cli/src/Spinner.php69
-rw-r--r--vendor/gipfl/cli/src/Tty.php132
-rw-r--r--vendor/gipfl/cli/src/TtyMode.php95
-rw-r--r--vendor/gipfl/curl/LICENSE21
-rw-r--r--vendor/gipfl/curl/composer.json29
-rw-r--r--vendor/gipfl/curl/src/CurlAsync.php338
-rw-r--r--vendor/gipfl/curl/src/CurlHandle.php76
-rw-r--r--vendor/gipfl/curl/src/RequestError.php44
-rw-r--r--vendor/gipfl/curl/src/ResponseParseError.php7
-rw-r--r--vendor/gipfl/data-type/composer.json24
-rw-r--r--vendor/gipfl/data-type/src/SetOfSettings.php79
-rw-r--r--vendor/gipfl/data-type/src/Settings.php103
-rw-r--r--vendor/gipfl/db-migration/composer.json16
-rw-r--r--vendor/gipfl/db-migration/src/Migration.php73
-rw-r--r--vendor/gipfl/db-migration/src/Migrations.php299
-rw-r--r--vendor/gipfl/diff/LICENSE21
-rw-r--r--vendor/gipfl/diff/composer.json25
-rw-r--r--vendor/gipfl/diff/public/css/diff.less133
-rw-r--r--vendor/gipfl/diff/src/HtmlRenderer/InlineDiff.php10
-rw-r--r--vendor/gipfl/diff/src/HtmlRenderer/SideBySideDiff.php10
-rw-r--r--vendor/gipfl/diff/src/PhpDiff.php147
-rw-r--r--vendor/gipfl/diff/src/PhpDiff/ArrayHelper.php54
-rw-r--r--vendor/gipfl/diff/src/PhpDiff/OpCodeHelper.php144
-rw-r--r--vendor/gipfl/diff/src/PhpDiff/Ratio.php139
-rw-r--r--vendor/gipfl/diff/src/PhpDiff/Renderer/AbstractRenderer.php44
-rw-r--r--vendor/gipfl/diff/src/PhpDiff/Renderer/Html/ArrayRenderer.php207
-rw-r--r--vendor/gipfl/diff/src/PhpDiff/Renderer/Html/Inline.php104
-rw-r--r--vendor/gipfl/diff/src/PhpDiff/Renderer/Html/SideBySide.php121
-rw-r--r--vendor/gipfl/diff/src/PhpDiff/Renderer/Text/Context.php98
-rw-r--r--vendor/gipfl/diff/src/PhpDiff/Renderer/Text/Unified.php52
-rw-r--r--vendor/gipfl/diff/src/PhpDiff/SequenceMatcher.php438
-rw-r--r--vendor/gipfl/format/composer.json28
-rw-r--r--vendor/gipfl/format/src/LocalDateFormat.php41
-rw-r--r--vendor/gipfl/format/src/LocalTimeFormat.php184
-rw-r--r--vendor/gipfl/format/src/LocaleAwareness.php94
-rw-r--r--vendor/gipfl/icinga-cli-daemon/composer.json24
-rw-r--r--vendor/gipfl/icinga-cli-daemon/src/DbResourceConfigWatch.php163
-rw-r--r--vendor/gipfl/icinga-cli-daemon/src/FinishedProcessState.php66
-rw-r--r--vendor/gipfl/icinga-cli-daemon/src/IcingaCli.php144
-rw-r--r--vendor/gipfl/icinga-cli-daemon/src/IcingaCliRpc.php45
-rw-r--r--vendor/gipfl/icinga-cli-daemon/src/IcingaCliRunner.php88
-rw-r--r--vendor/gipfl/icinga-cli-daemon/src/RetryUnless.php244
-rw-r--r--vendor/gipfl/icinga-cli-daemon/src/StateMachine.php113
-rw-r--r--vendor/gipfl/icingaweb2/composer.json21
-rw-r--r--vendor/gipfl/icingaweb2/public/css/11-action-bar.less105
-rw-r--r--vendor/gipfl/icingaweb2/public/css/12-quicksearch.less17
-rw-r--r--vendor/gipfl/icingaweb2/src/CompatController.php581
-rw-r--r--vendor/gipfl/icingaweb2/src/Controller/Extension/AutoRefreshHelper.php30
-rw-r--r--vendor/gipfl/icingaweb2/src/Controller/Extension/ConfigHelper.php46
-rw-r--r--vendor/gipfl/icingaweb2/src/Controller/Extension/ControlsAndContentHelper.php173
-rw-r--r--vendor/gipfl/icingaweb2/src/Data/Paginatable.php64
-rw-r--r--vendor/gipfl/icingaweb2/src/Data/SimpleQueryPaginationAdapter.php68
-rw-r--r--vendor/gipfl/icingaweb2/src/FakeRequest.php32
-rw-r--r--vendor/gipfl/icingaweb2/src/Icon.php27
-rw-r--r--vendor/gipfl/icingaweb2/src/IconHelper.php89
-rw-r--r--vendor/gipfl/icingaweb2/src/Img.php83
-rw-r--r--vendor/gipfl/icingaweb2/src/Link.php85
-rw-r--r--vendor/gipfl/icingaweb2/src/Table/Extension/MultiSelect.php29
-rw-r--r--vendor/gipfl/icingaweb2/src/Table/Extension/QuickSearch.php74
-rw-r--r--vendor/gipfl/icingaweb2/src/Table/Extension/ZfSortablePriority.php263
-rw-r--r--vendor/gipfl/icingaweb2/src/Table/QueryBasedTable.php281
-rw-r--r--vendor/gipfl/icingaweb2/src/Table/SimpleQueryBasedTable.php34
-rw-r--r--vendor/gipfl/icingaweb2/src/Table/ZfQueryBasedTable.php149
-rw-r--r--vendor/gipfl/icingaweb2/src/Translator.php26
-rw-r--r--vendor/gipfl/icingaweb2/src/Url.php162
-rw-r--r--vendor/gipfl/icingaweb2/src/Widget/ActionBar.php25
-rw-r--r--vendor/gipfl/icingaweb2/src/Widget/Content.php14
-rw-r--r--vendor/gipfl/icingaweb2/src/Widget/Controls.php163
-rw-r--r--vendor/gipfl/icingaweb2/src/Widget/ControlsAndContent.php60
-rw-r--r--vendor/gipfl/icingaweb2/src/Widget/ListItem.php26
-rw-r--r--vendor/gipfl/icingaweb2/src/Widget/NameValueTable.php29
-rw-r--r--vendor/gipfl/icingaweb2/src/Widget/Paginator.php463
-rw-r--r--vendor/gipfl/icingaweb2/src/Widget/Tabs.php44
-rw-r--r--vendor/gipfl/icingaweb2/src/Zf1/Db/CountQuery.php93
-rw-r--r--vendor/gipfl/icingaweb2/src/Zf1/Db/FilterRenderer.php335
-rw-r--r--vendor/gipfl/icingaweb2/src/Zf1/Db/SelectPaginationAdapter.php111
-rw-r--r--vendor/gipfl/icingaweb2/src/Zf1/SimpleViewRenderer.php115
-rw-r--r--vendor/gipfl/influxdb/LICENSE21
-rw-r--r--vendor/gipfl/influxdb/composer.json25
-rw-r--r--vendor/gipfl/influxdb/src/ChunkedInfluxDbWriter.php158
-rw-r--r--vendor/gipfl/influxdb/src/DataPoint.php63
-rw-r--r--vendor/gipfl/influxdb/src/Escape.php67
-rw-r--r--vendor/gipfl/influxdb/src/InfluxDbConnection.php24
-rw-r--r--vendor/gipfl/influxdb/src/InfluxDbConnectionFactory.php38
-rw-r--r--vendor/gipfl/influxdb/src/InfluxDbConnectionV1.php311
-rw-r--r--vendor/gipfl/influxdb/src/InfluxDbConnectionV2.php270
-rw-r--r--vendor/gipfl/influxdb/src/InfluxDbQueryResult.php65
-rw-r--r--vendor/gipfl/influxdb/src/LineProtocol.php63
-rw-r--r--vendor/gipfl/json/composer.json20
-rw-r--r--vendor/gipfl/json/src/JsonDecodeException.php7
-rw-r--r--vendor/gipfl/json/src/JsonEncodeException.php7
-rw-r--r--vendor/gipfl/json/src/JsonException.php55
-rw-r--r--vendor/gipfl/json/src/JsonSerialization.php14
-rw-r--r--vendor/gipfl/json/src/JsonString.php68
-rw-r--r--vendor/gipfl/json/src/SerializationHelper.php55
-rw-r--r--vendor/gipfl/linux-health/composer.json23
-rw-r--r--vendor/gipfl/linux-health/src/Cpu.php59
-rw-r--r--vendor/gipfl/linux-health/src/Memory.php52
-rw-r--r--vendor/gipfl/linux-health/src/Network.php36
-rw-r--r--vendor/gipfl/log/LICENSE21
-rw-r--r--vendor/gipfl/log/composer.json31
-rw-r--r--vendor/gipfl/log/src/AdditionalContextLogger.php25
-rw-r--r--vendor/gipfl/log/src/DummyLogger.php44
-rw-r--r--vendor/gipfl/log/src/Filter/LogLevelFilter.php39
-rw-r--r--vendor/gipfl/log/src/Formatter/StdOutFormatter.php34
-rw-r--r--vendor/gipfl/log/src/IcingaWeb/IcingaLogger.php29
-rw-r--r--vendor/gipfl/log/src/IcingaWeb/LoggerLogWriter.php32
-rw-r--r--vendor/gipfl/log/src/LogFilter.php14
-rw-r--r--vendor/gipfl/log/src/LogFormatter.php8
-rw-r--r--vendor/gipfl/log/src/LogLevel.php66
-rw-r--r--vendor/gipfl/log/src/LogWriter.php8
-rw-r--r--vendor/gipfl/log/src/LogWriterWithContext.php8
-rw-r--r--vendor/gipfl/log/src/Logger.php169
-rw-r--r--vendor/gipfl/log/src/PrefixLogger.php25
-rw-r--r--vendor/gipfl/log/src/Writer/JournaldLogger.php61
-rw-r--r--vendor/gipfl/log/src/Writer/JsonRpcConnectionWriter.php51
-rw-r--r--vendor/gipfl/log/src/Writer/JsonRpcWriter.php54
-rw-r--r--vendor/gipfl/log/src/Writer/ProxyLogWriter.php14
-rw-r--r--vendor/gipfl/log/src/Writer/SyslogWriter.php34
-rw-r--r--vendor/gipfl/log/src/Writer/SystemdStdoutWriter.php61
-rw-r--r--vendor/gipfl/log/src/Writer/WritableStreamWriter.php44
-rw-r--r--vendor/gipfl/openrpc/composer.json24
-rw-r--r--vendor/gipfl/openrpc/src/Components.php68
-rw-r--r--vendor/gipfl/openrpc/src/Contact.php49
-rw-r--r--vendor/gipfl/openrpc/src/ContentDescriptor.php73
-rw-r--r--vendor/gipfl/openrpc/src/Error.php54
-rw-r--r--vendor/gipfl/openrpc/src/Example.php36
-rw-r--r--vendor/gipfl/openrpc/src/ExamplePairing.php30
-rw-r--r--vendor/gipfl/openrpc/src/ExternalDocumentation.php37
-rw-r--r--vendor/gipfl/openrpc/src/Info.php69
-rw-r--r--vendor/gipfl/openrpc/src/License.php37
-rw-r--r--vendor/gipfl/openrpc/src/Link.php91
-rw-r--r--vendor/gipfl/openrpc/src/Method.php133
-rw-r--r--vendor/gipfl/openrpc/src/OpenRpcDocument.php75
-rw-r--r--vendor/gipfl/openrpc/src/Reference.php32
-rw-r--r--vendor/gipfl/openrpc/src/Reflection/MetaDataClass.php76
-rw-r--r--vendor/gipfl/openrpc/src/Reflection/MetaDataMethod.php111
-rw-r--r--vendor/gipfl/openrpc/src/Reflection/MetaDataParameter.php46
-rw-r--r--vendor/gipfl/openrpc/src/Reflection/MetaDataTagParser.php60
-rw-r--r--vendor/gipfl/openrpc/src/Reflection/MetaDataTagSet.php69
-rw-r--r--vendor/gipfl/openrpc/src/Reflection/MethodCommentParser.php163
-rw-r--r--vendor/gipfl/openrpc/src/Reflection/ParamTag.php41
-rw-r--r--vendor/gipfl/openrpc/src/Reflection/ReturnTag.php7
-rw-r--r--vendor/gipfl/openrpc/src/Reflection/Tag.php37
-rw-r--r--vendor/gipfl/openrpc/src/Reflection/ThrowsTag.php7
-rw-r--r--vendor/gipfl/openrpc/src/Reflection/TypeDescriptionTag.php27
-rw-r--r--vendor/gipfl/openrpc/src/Server.php74
-rw-r--r--vendor/gipfl/openrpc/src/ServerVariable.php46
-rw-r--r--vendor/gipfl/openrpc/src/SimpleJsonSerializer.php14
-rw-r--r--vendor/gipfl/openrpc/src/TagObject.php48
-rw-r--r--vendor/gipfl/process/composer.json25
-rw-r--r--vendor/gipfl/process/src/FinishedProcessState.php66
-rw-r--r--vendor/gipfl/process/src/ProcessInfo.php89
-rw-r--r--vendor/gipfl/process/src/ProcessKiller.php81
-rw-r--r--vendor/gipfl/process/src/ProcessList.php44
-rw-r--r--vendor/gipfl/protocol-jsonrpc/LICENSE21
-rw-r--r--vendor/gipfl/protocol-jsonrpc/composer.json34
-rw-r--r--vendor/gipfl/protocol-jsonrpc/src/Connection.php310
-rw-r--r--vendor/gipfl/protocol-jsonrpc/src/Error.php199
-rw-r--r--vendor/gipfl/protocol-jsonrpc/src/Handler/FailingPacketHandler.php28
-rw-r--r--vendor/gipfl/protocol-jsonrpc/src/Handler/JsonRpcHandler.php23
-rw-r--r--vendor/gipfl/protocol-jsonrpc/src/Handler/NamespacedPacketHandler.php217
-rw-r--r--vendor/gipfl/protocol-jsonrpc/src/JsonRpcConnection.php241
-rw-r--r--vendor/gipfl/protocol-jsonrpc/src/Notification.php98
-rw-r--r--vendor/gipfl/protocol-jsonrpc/src/Packet.php226
-rw-r--r--vendor/gipfl/protocol-jsonrpc/src/PacketHandler.php11
-rw-r--r--vendor/gipfl/protocol-jsonrpc/src/Request.php59
-rw-r--r--vendor/gipfl/protocol-jsonrpc/src/Response.php128
-rw-r--r--vendor/gipfl/protocol-jsonrpc/src/TestCase.php44
-rw-r--r--vendor/gipfl/protocol-netstring/LICENSE21
-rw-r--r--vendor/gipfl/protocol-netstring/composer.json25
-rw-r--r--vendor/gipfl/protocol-netstring/src/StreamWrapper.php113
-rw-r--r--vendor/gipfl/protocol/LICENSE21
-rw-r--r--vendor/gipfl/protocol/composer.json26
-rw-r--r--vendor/gipfl/protocol/src/Exception/ProtocolError.php9
-rw-r--r--vendor/gipfl/protocol/src/Generic/AbstractStreamWrapper.php139
-rw-r--r--vendor/gipfl/react-utils/LICENSE21
-rw-r--r--vendor/gipfl/react-utils/composer.json24
-rw-r--r--vendor/gipfl/react-utils/src/RetryUnless.php256
-rw-r--r--vendor/gipfl/simple-daemon/composer.json29
-rw-r--r--vendor/gipfl/simple-daemon/src/Daemon.php156
-rw-r--r--vendor/gipfl/simple-daemon/src/DaemonState.php172
-rw-r--r--vendor/gipfl/simple-daemon/src/DaemonTask.php20
-rw-r--r--vendor/gipfl/simple-daemon/src/SystemdAwareTask.php13
-rw-r--r--vendor/gipfl/socket/composer.json29
-rw-r--r--vendor/gipfl/socket/src/ConnectionList.php87
-rw-r--r--vendor/gipfl/socket/src/UnixSocketInspection.php89
-rw-r--r--vendor/gipfl/socket/src/UnixSocketPeer.php102
-rw-r--r--vendor/gipfl/stream/composer.json26
-rw-r--r--vendor/gipfl/stream/src/BufferedLineReader.php100
-rw-r--r--vendor/gipfl/systemd/LICENSE21
-rw-r--r--vendor/gipfl/systemd/composer.json26
-rw-r--r--vendor/gipfl/systemd/src/NotificationSocket.php122
-rw-r--r--vendor/gipfl/systemd/src/NotifySystemD.php292
-rw-r--r--vendor/gipfl/systemd/src/systemd.php19
-rw-r--r--vendor/gipfl/translation/LICENSE21
-rw-r--r--vendor/gipfl/translation/composer.json20
-rw-r--r--vendor/gipfl/translation/src/NoTranslator.php11
-rw-r--r--vendor/gipfl/translation/src/StaticTranslator.php31
-rw-r--r--vendor/gipfl/translation/src/TranslationHelper.php37
-rw-r--r--vendor/gipfl/translation/src/TranslatorInterface.php8
-rw-r--r--vendor/gipfl/translation/src/WrapTranslator.php26
-rw-r--r--vendor/gipfl/web/LICENSE21
-rw-r--r--vendor/gipfl/web/composer.json25
-rw-r--r--vendor/gipfl/web/public/css/01-gipfl-base.less8
-rw-r--r--vendor/gipfl/web/public/css/21-gipfl-collapsible.less55
-rw-r--r--vendor/gipfl/web/public/css/21-gipfl-widget-hint.less57
-rw-r--r--vendor/gipfl/web/public/css/31-gipfl-name-value-table.less27
-rw-r--r--vendor/gipfl/web/public/css/40-gipfl-form.less69
-rw-r--r--vendor/gipfl/web/public/css/41-director-form.less339
-rw-r--r--vendor/gipfl/web/public/css/42-director-extensible-set.less33
-rw-r--r--vendor/gipfl/web/public/css/43-inline-form.less29
-rw-r--r--vendor/gipfl/web/public/css/81-phpdiff.less97
-rw-r--r--vendor/gipfl/web/public/js/module.js69
-rw-r--r--vendor/gipfl/web/src/Form.php281
-rw-r--r--vendor/gipfl/web/src/Form/Decorator/DdDtDecorator.php158
-rw-r--r--vendor/gipfl/web/src/Form/Element/Boolean.php39
-rw-r--r--vendor/gipfl/web/src/Form/Element/MultiSelect.php119
-rw-r--r--vendor/gipfl/web/src/Form/Element/Password.php11
-rw-r--r--vendor/gipfl/web/src/Form/Element/TextWithActionButton.php104
-rw-r--r--vendor/gipfl/web/src/Form/Feature/NextConfirmCancel.php153
-rw-r--r--vendor/gipfl/web/src/Form/Validator/AlwaysFailValidator.php16
-rw-r--r--vendor/gipfl/web/src/Form/Validator/PhpSessionBasedCsrfTokenValidator.php34
-rw-r--r--vendor/gipfl/web/src/Form/Validator/SimpleValidator.php27
-rw-r--r--vendor/gipfl/web/src/HtmlHelper.php29
-rw-r--r--vendor/gipfl/web/src/InlineForm.php10
-rw-r--r--vendor/gipfl/web/src/Table/NameValueTable.php47
-rw-r--r--vendor/gipfl/web/src/Widget/CollapsibleList.php74
-rw-r--r--vendor/gipfl/web/src/Widget/ConfigDiff.php106
-rw-r--r--vendor/gipfl/web/src/Widget/Hint.php45
-rw-r--r--vendor/gipfl/web/src/vendor/php-diff/lib/Diff.php179
-rw-r--r--vendor/gipfl/web/src/vendor/php-diff/lib/Diff/Renderer/Abstract.php82
-rw-r--r--vendor/gipfl/web/src/vendor/php-diff/lib/Diff/Renderer/Html/Array.php230
-rw-r--r--vendor/gipfl/web/src/vendor/php-diff/lib/Diff/Renderer/Html/Inline.php143
-rw-r--r--vendor/gipfl/web/src/vendor/php-diff/lib/Diff/Renderer/Html/SideBySide.php163
-rw-r--r--vendor/gipfl/web/src/vendor/php-diff/lib/Diff/Renderer/Text/Context.php128
-rw-r--r--vendor/gipfl/web/src/vendor/php-diff/lib/Diff/Renderer/Text/Unified.php87
-rw-r--r--vendor/gipfl/web/src/vendor/php-diff/lib/Diff/SequenceMatcher.php742
-rw-r--r--vendor/gipfl/zfdb/composer.json16
-rw-r--r--vendor/gipfl/zfdb/src/Adapter/Adapter.php1267
-rw-r--r--vendor/gipfl/zfdb/src/Adapter/Db2.php792
-rw-r--r--vendor/gipfl/zfdb/src/Adapter/Exception/AdapterException.php50
-rw-r--r--vendor/gipfl/zfdb/src/Adapter/Exception/AdapterExceptionDb2.php42
-rw-r--r--vendor/gipfl/zfdb/src/Adapter/Exception/AdapterExceptionMysqli.php30
-rw-r--r--vendor/gipfl/zfdb/src/Adapter/Exception/AdapterExceptionOracle.php51
-rw-r--r--vendor/gipfl/zfdb/src/Adapter/Exception/AdapterExceptionSqlsrv.php55
-rw-r--r--vendor/gipfl/zfdb/src/Adapter/Mysqli.php499
-rw-r--r--vendor/gipfl/zfdb/src/Adapter/Oracle.php595
-rw-r--r--vendor/gipfl/zfdb/src/Adapter/Pdo/Ibm.php352
-rw-r--r--vendor/gipfl/zfdb/src/Adapter/Pdo/Ibm/Db2.php215
-rw-r--r--vendor/gipfl/zfdb/src/Adapter/Pdo/Ibm/Ids.php290
-rw-r--r--vendor/gipfl/zfdb/src/Adapter/Pdo/Mssql.php384
-rw-r--r--vendor/gipfl/zfdb/src/Adapter/Pdo/Mysql.php258
-rw-r--r--vendor/gipfl/zfdb/src/Adapter/Pdo/Oci.php367
-rw-r--r--vendor/gipfl/zfdb/src/Adapter/Pdo/PdoAdapter.php367
-rw-r--r--vendor/gipfl/zfdb/src/Adapter/Pdo/Pgsql.php318
-rw-r--r--vendor/gipfl/zfdb/src/Adapter/Pdo/Sqlite.php293
-rw-r--r--vendor/gipfl/zfdb/src/Adapter/Sqlsrv.php640
-rw-r--r--vendor/gipfl/zfdb/src/Db.php248
-rw-r--r--vendor/gipfl/zfdb/src/Exception/DbException.php29
-rw-r--r--vendor/gipfl/zfdb/src/Exception/SelectException.php27
-rw-r--r--vendor/gipfl/zfdb/src/Expr.php70
-rw-r--r--vendor/gipfl/zfdb/src/Profiler.php463
-rw-r--r--vendor/gipfl/zfdb/src/Profiler/ProfilerException.php29
-rw-r--r--vendor/gipfl/zfdb/src/Profiler/ProfilerQuery.php206
-rw-r--r--vendor/gipfl/zfdb/src/Select.php1350
-rw-r--r--vendor/gipfl/zfdb/src/Statement.php462
-rw-r--r--vendor/gipfl/zfdb/src/Statement/Db2Statement.php334
-rw-r--r--vendor/gipfl/zfdb/src/Statement/Exception/StatementException.php48
-rw-r--r--vendor/gipfl/zfdb/src/Statement/Exception/StatementExceptionDb2.php50
-rw-r--r--vendor/gipfl/zfdb/src/Statement/Exception/StatementExceptionMysqli.php27
-rw-r--r--vendor/gipfl/zfdb/src/Statement/Exception/StatementExceptionOracle.php49
-rw-r--r--vendor/gipfl/zfdb/src/Statement/Exception/StatementExceptionSqlsrv.php55
-rw-r--r--vendor/gipfl/zfdb/src/Statement/MysqliStatement.php345
-rw-r--r--vendor/gipfl/zfdb/src/Statement/OracleStatement.php519
-rw-r--r--vendor/gipfl/zfdb/src/Statement/Pdo/IbmPdoStatement.php86
-rw-r--r--vendor/gipfl/zfdb/src/Statement/Pdo/OciPdoStatement.php83
-rw-r--r--vendor/gipfl/zfdb/src/Statement/PdoStatement.php417
-rw-r--r--vendor/gipfl/zfdb/src/Statement/SqlsrvStatement.php423
-rw-r--r--vendor/gipfl/zfdb/src/Statement/StatementInterface.php198
-rw-r--r--vendor/gipfl/zfdbstore/composer.json16
-rw-r--r--vendor/gipfl/zfdbstore/src/BaseStore.php112
-rw-r--r--vendor/gipfl/zfdbstore/src/DbStorable.php79
-rw-r--r--vendor/gipfl/zfdbstore/src/DbStorableInterface.php36
-rw-r--r--vendor/gipfl/zfdbstore/src/NotFoundError.php9
-rw-r--r--vendor/gipfl/zfdbstore/src/Storable.php323
-rw-r--r--vendor/gipfl/zfdbstore/src/StorableInterface.php44
-rw-r--r--vendor/gipfl/zfdbstore/src/Store.php24
-rw-r--r--vendor/gipfl/zfdbstore/src/ZfDbStore.php241
-rw-r--r--vendor/psr/log/LICENSE19
-rw-r--r--vendor/psr/log/Psr/Log/AbstractLogger.php128
-rw-r--r--vendor/psr/log/Psr/Log/InvalidArgumentException.php7
-rw-r--r--vendor/psr/log/Psr/Log/LogLevel.php18
-rw-r--r--vendor/psr/log/Psr/Log/LoggerAwareInterface.php18
-rw-r--r--vendor/psr/log/Psr/Log/LoggerAwareTrait.php26
-rw-r--r--vendor/psr/log/Psr/Log/LoggerInterface.php125
-rw-r--r--vendor/psr/log/Psr/Log/LoggerTrait.php142
-rw-r--r--vendor/psr/log/Psr/Log/NullLogger.php30
-rw-r--r--vendor/psr/log/Psr/Log/Test/DummyTest.php18
-rw-r--r--vendor/psr/log/Psr/Log/Test/LoggerInterfaceTest.php138
-rw-r--r--vendor/psr/log/Psr/Log/Test/TestLogger.php147
-rw-r--r--vendor/psr/log/composer.json26
337 files changed, 41328 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..b8ab40e
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,6 @@
+/vendor/
+/.idea/
+.*.sw[op]
+composer.lock
+public/css/combined.less
+public/js/combined.js
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..58005ec
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+The MIT License
+
+Copyright (c) 2018 Icinga GmbH https://www.icinga.com
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..c99da5f
--- /dev/null
+++ b/README.md
@@ -0,0 +1,76 @@
+Icinga Web 2 - Incubator
+========================
+
+This repository ships bleeding edge libraries useful for Icinga Web 2 modules.
+Please download the latest release and install it like any other module.
+
+> **HINT**: Do NOT install the GIT master, it will not work! Checking out a
+> branch like `stable/0.18.0` or a tag like `v0.18.0` is fine.
+
+Sample Tarball installation
+---------------------------
+
+```sh
+MODULE_NAME=incubator
+MODULE_VERSION=v0.20.0
+MODULES_PATH="/usr/share/icingaweb2/modules"
+MODULE_PATH="${MODULES_PATH}/${MODULE_NAME}"
+RELEASES="https://github.com/Icinga/icingaweb2-module-${MODULE_NAME}/archive"
+mkdir "$MODULE_PATH" \
+&& wget -q $RELEASES/${MODULE_VERSION}.tar.gz -O - \
+ | tar xfz - -C "$MODULE_PATH" --strip-components 1
+icingacli module enable "${MODULE_NAME}"
+```
+
+Sample GIT installation
+-----------------------
+
+```sh
+MODULE_NAME=incubator
+MODULE_VERSION=v0.20.0
+REPO="https://github.com/Icinga/icingaweb2-module-${MODULE_NAME}"
+MODULES_PATH="/usr/share/icingaweb2/modules"
+git clone ${REPO} "${MODULES_PATH}/${MODULE_NAME}" --branch "${MODULE_VERSION}"
+icingacli module enable "${MODULE_NAME}"
+```
+
+Developer Documentation
+-----------------------
+
+### Add a new dependency
+
+ composer require author/library:version
+
+### Create a new release
+
+ ./bin/make-release.sh <version>
+
+e.g.
+
+ ./bin/make-release.sh 0.20.0
+
+Changes
+-------
+
+### v0.20.0
+
+* improved am/pm formatting
+* support db expressions in filter rendering
+* fix background in action bar dropdowns
+* JsonRPC no supports proxy handlers
+* dbstore improved modification detection
+
+### v0.19.0
+
+* improved ProcessInfo serialization
+* allow to use Cli\Screen w/o CLI
+* curl: fix PHP 8.1 support in specific error conditions
+* InfluxDB: fix v2 support
+* InfluxDB: body compression
+* InfluxDB: add header for debugging purposes
+
+### v0.18.0
+
+* cosmetic changes for Icinga Web
+* Settings can now be compared
+* fix some zfdb exceptions on 8.1
diff --git a/bin/make-release.sh b/bin/make-release.sh
new file mode 100755
index 0000000..fa3e48e
--- /dev/null
+++ b/bin/make-release.sh
@@ -0,0 +1,51 @@
+#!/bin/bash
+
+VERSION="$1"
+
+if [[ -z $VERSION ]]; then
+ echo "USAGE: $0 <version>"
+ echo " e.g.: $0 0.1.0"
+ exit 1
+fi
+
+function fail {
+ local msg="$1"
+ echo "ERROR: $msg"
+ exit 1
+}
+
+TAG=$(git tag | grep -c "$VERSION$")
+
+if [[ "$TAG" -ne "0" ]]; then
+ echo -n "Version $VERSION has already been tagged: "
+ git tag | grep "$VERSION"
+ exit 1
+fi
+
+BRANCH="stable/$VERSION"
+git checkout -b "$BRANCH"
+git rm -rf vendor
+rm -rf vendor
+rm -f composer.lock
+composer install || fail "composer install failed"
+find vendor/ -type f -name "*.php" -or -name "*.js" -or -name "*.css" -or -name "*.less" -or -name "*.json" \
+ | grep -v '/examples/' \
+ | grep -v '/example/' \
+ | grep -v '/tests/' \
+ | grep -v '/test/' \
+ | xargs -L1 git add -f
+find vendor/ -type f -name LICENSE | xargs -L1 git add -f
+find public/ -type f | xargs -L1 git add -f
+sed -i.bak "s/^Version:.*/Version: $VERSION/" module.info && rm -f module.info.bak
+git add module.info
+git add composer.lock -f
+git commit -m "Version v$VERSION"
+
+rm -rf vendor
+git checkout vendor
+composer validate --no-check-all --strict || fail "Composer validate failed"
+
+git tag -a v$VERSION -m "Version v$VERSION"
+echo "Finished, tagged v$VERSION"
+echo "Now please run:"
+echo "git push -u origin "$BRANCH":"$BRANCH" && git push --tags"
diff --git a/bin/refresh-public.sh b/bin/refresh-public.sh
new file mode 100755
index 0000000..77286e1
--- /dev/null
+++ b/bin/refresh-public.sh
@@ -0,0 +1,3 @@
+#!/bin/bash
+find -L vendor/*/*/public/css -type f -name "*.less" -exec cat {} \; 2>/dev/null > public/css/combined.less
+find -L vendor/*/*/public/js -type f -name "*.js" -exec cat {} \; 2>/dev/null > public/js/combined.js
diff --git a/composer.json b/composer.json
new file mode 100644
index 0000000..e9974eb
--- /dev/null
+++ b/composer.json
@@ -0,0 +1,52 @@
+{
+ "name": "icinga/incubator",
+ "type": "library",
+ "description": "Icinga Web 2 - Incubator for bleeding edge libraries",
+ "homepage": "https://github.com/Icinga/icingaweb2-module-incubator",
+ "license": "MIT",
+ "authors": [{
+ "name": "Thomas Gelf",
+ "email": "thomas@gelf.net"
+ }],
+ "config": {
+ "sort-packages": true,
+ "platform": {
+ "php": "5.6.3"
+ }
+ },
+ "require": {
+ "php": ">=5.6.3",
+ "ext-ctype": "*",
+ "gipfl/calendar": ">=0.3",
+ "gipfl/cli": ">=0.6",
+ "gipfl/curl": ">=0.3",
+ "gipfl/data-type": ">=0.3.0",
+ "gipfl/db-migration": ">=0.1.1",
+ "gipfl/diff": ">=0.3",
+ "gipfl/format": ">=0.4",
+ "gipfl/icinga-bundles": ">=0.7",
+ "gipfl/icinga-cli-daemon": ">=0.3.2",
+ "gipfl/icingaweb2": ">=0.10",
+ "gipfl/influxdb": ">=0.5",
+ "gipfl/json": ">=0.2",
+ "gipfl/linux-health": ">=0.2",
+ "gipfl/log": ">=0.7",
+ "gipfl/process": ">=0.4",
+ "gipfl/protocol-jsonrpc": ">=0.11",
+ "gipfl/protocol-netstring": ">=0.1.1",
+ "gipfl/react-utils": ">=0.3",
+ "gipfl/simple-daemon": ">=0.6",
+ "gipfl/socket": ">=0.4",
+ "gipfl/stream": ">=0.2",
+ "gipfl/systemd": ">=0.3",
+ "gipfl/translation": ">=0.1.1",
+ "gipfl/web": ">=0.11",
+ "gipfl/zfdb": ">=0.4",
+ "gipfl/zfdbstore": ">=0.3"
+ },
+ "scripts": {
+ "post-update-cmd": [
+ "bin/refresh-public.sh"
+ ]
+ }
+}
diff --git a/composer.lock b/composer.lock
new file mode 100644
index 0000000..0a04339
--- /dev/null
+++ b/composer.lock
@@ -0,0 +1,1283 @@
+{
+ "_readme": [
+ "This file locks the dependencies of your project to a known state",
+ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
+ "This file is @generated automatically"
+ ],
+ "content-hash": "0f8615637bf4546ebf9fb8223d79bd6f",
+ "packages": [
+ {
+ "name": "gipfl/calendar",
+ "version": "v0.3.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/gipfl/calendar.git",
+ "reference": "fd57d12aa97dd4c2a115e5cae36097ffaed220b8"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/gipfl/calendar/zipball/fd57d12aa97dd4c2a115e5cae36097ffaed220b8",
+ "reference": "fd57d12aa97dd4c2a115e5cae36097ffaed220b8",
+ "shasum": ""
+ },
+ "require": {
+ "gipfl/format": ">=0.3",
+ "gipfl/icingaweb2": ">=0.4.0",
+ "gipfl/translation": ">=0.1.1",
+ "php": ">=5.4.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "gipfl\\Calendar\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "authors": [
+ {
+ "name": "Thomas Gelf",
+ "email": "thomas@gelf.net"
+ }
+ ],
+ "description": "Calendar Utils",
+ "homepage": "https://github.com/gipfl/calendar",
+ "keywords": [
+ "calendar"
+ ],
+ "support": {
+ "issues": "https://github.com/gipfl/calendar/issues",
+ "source": "https://github.com/gipfl/calendar/tree/v0.3.1"
+ },
+ "time": "2023-01-16T14:08:24+00:00"
+ },
+ {
+ "name": "gipfl/cli",
+ "version": "v0.6.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/gipfl/cli.git",
+ "reference": "536dec1fa605aa98aef32421cd70bf71e46406d0"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/gipfl/cli/zipball/536dec1fa605aa98aef32421cd70bf71e46406d0",
+ "reference": "536dec1fa605aa98aef32421cd70bf71e46406d0",
+ "shasum": ""
+ },
+ "require": {
+ "ext-mbstring": "*",
+ "ext-pcntl": "*",
+ "ext-posix": "*",
+ "php": ">=5.6.0",
+ "react/promise": "^2",
+ "react/stream": ">=1.1"
+ },
+ "require-dev": {
+ "react/child-process": ">=0.6"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "gipfl\\Cli\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Thomas Gelf",
+ "email": "thomas@gelf.net"
+ }
+ ],
+ "description": "CLI utilities",
+ "support": {
+ "issues": "https://github.com/gipfl/cli/issues",
+ "source": "https://github.com/gipfl/cli/tree/v0.6.0"
+ },
+ "time": "2022-10-07T13:11:48+00:00"
+ },
+ {
+ "name": "gipfl/curl",
+ "version": "v0.3.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/gipfl/curl.git",
+ "reference": "bc1373dff8ab247c734d019d0fe7d94521660b1f"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/gipfl/curl/zipball/bc1373dff8ab247c734d019d0fe7d94521660b1f",
+ "reference": "bc1373dff8ab247c734d019d0fe7d94521660b1f",
+ "shasum": ""
+ },
+ "require": {
+ "ext-curl": "*",
+ "guzzlehttp/psr7": ">=1.6",
+ "php": ">=5.6.3",
+ "psr/http-message": "^1.0",
+ "react/event-loop": ">=1.0",
+ "react/promise": ">=2",
+ "react/stream": ">=1.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "gipfl\\Curl\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Thomas Gelf",
+ "email": "thomas@gelf.net"
+ }
+ ],
+ "description": "ReactPHP-friendly async CURL abstraction",
+ "support": {
+ "issues": "https://github.com/gipfl/curl/issues",
+ "source": "https://github.com/gipfl/curl/tree/v0.3.0"
+ },
+ "time": "2022-10-07T13:34:17+00:00"
+ },
+ {
+ "name": "gipfl/data-type",
+ "version": "v0.3.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/gipfl/data-type.git",
+ "reference": "eb81eaeef93adb15c6c5522154516eae0e2a8e48"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/gipfl/data-type/zipball/eb81eaeef93adb15c6c5522154516eae0e2a8e48",
+ "reference": "eb81eaeef93adb15c6c5522154516eae0e2a8e48",
+ "shasum": ""
+ },
+ "require": {
+ "gipfl/json": ">=0.2.0",
+ "php": ">=5.6.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "gipfl\\DataType\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Thomas Gelf",
+ "email": "thomas@gelf.net"
+ }
+ ],
+ "description": "Serializable Data Types",
+ "support": {
+ "issues": "https://github.com/gipfl/data-type/issues",
+ "source": "https://github.com/gipfl/data-type/tree/v0.3.0"
+ },
+ "time": "2022-09-01T07:38:42+00:00"
+ },
+ {
+ "name": "gipfl/db-migration",
+ "version": "v0.1.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/gipfl/db-migration.git",
+ "reference": "12be94b0c83a5e018c9d3d42fe8e98b4cbe248ee"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/gipfl/db-migration/zipball/12be94b0c83a5e018c9d3d42fe8e98b4cbe248ee",
+ "reference": "12be94b0c83a5e018c9d3d42fe8e98b4cbe248ee",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.6"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "gipfl\\DbMigration\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "description": "Simple DB migration helper",
+ "support": {
+ "issues": "https://github.com/gipfl/db-migration/issues",
+ "source": "https://github.com/gipfl/db-migration/tree/v0.1.1"
+ },
+ "time": "2021-11-11T15:40:11+00:00"
+ },
+ {
+ "name": "gipfl/diff",
+ "version": "v0.3.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/gipfl/diff.git",
+ "reference": "f9decb9cf20735d6cbe6f508aafd8a720922804b"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/gipfl/diff/zipball/f9decb9cf20735d6cbe6f508aafd8a720922804b",
+ "reference": "f9decb9cf20735d6cbe6f508aafd8a720922804b",
+ "shasum": ""
+ },
+ "require": {
+ "ext-mbstring": "*",
+ "ipl/html": ">=0.2",
+ "php": ">=5.6.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "gipfl\\Diff\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Thomas Gelf",
+ "email": "thomas@gelf.net"
+ }
+ ],
+ "description": "php-diff wrapper supporting ipl/html",
+ "support": {
+ "issues": "https://github.com/gipfl/diff/issues",
+ "source": "https://github.com/gipfl/diff/tree/v0.3.0"
+ },
+ "time": "2022-04-18T11:39:03+00:00"
+ },
+ {
+ "name": "gipfl/format",
+ "version": "v0.4.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/gipfl/format.git",
+ "reference": "87bd240f80baac73d18aeaf11ef389d94c56c8a0"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/gipfl/format/zipball/87bd240f80baac73d18aeaf11ef389d94c56c8a0",
+ "reference": "87bd240f80baac73d18aeaf11ef389d94c56c8a0",
+ "shasum": ""
+ },
+ "require": {
+ "ext-intl": "*",
+ "php": ">=5.6.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^9.3 || ^7.5 || ^6.5 || ^5.7",
+ "squizlabs/php_codesniffer": "^3.6"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "gipfl\\Format\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Thomas Gelf",
+ "email": "thomas@gelf.net"
+ }
+ ],
+ "description": "Arbitrary collection of Format helpers",
+ "support": {
+ "issues": "https://github.com/gipfl/format/issues",
+ "source": "https://github.com/gipfl/format/tree/v0.4.0"
+ },
+ "time": "2023-01-16T11:46:49+00:00"
+ },
+ {
+ "name": "gipfl/icinga-bundles",
+ "version": "v0.7.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/gipfl/icinga-bundles.git",
+ "reference": "d28e12218c07174795e07d7ad235d3fac2a6be53"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/gipfl/icinga-bundles/zipball/d28e12218c07174795e07d7ad235d3fac2a6be53",
+ "reference": "d28e12218c07174795e07d7ad235d3fac2a6be53",
+ "shasum": ""
+ },
+ "require": {
+ "ext-json": "*"
+ },
+ "replace": {
+ "clue/block-react": "v1.4.0",
+ "clue/buzz-react": "v2.7.0",
+ "clue/connection-manager-extra": "v1.1.0",
+ "clue/http-proxy-react": "v1.5.0",
+ "clue/mq-react": "v1.2.0",
+ "clue/redis-protocol": "v0.3.1",
+ "clue/redis-react": "v2.3.0",
+ "clue/soap-react": "v1.0.0",
+ "clue/socket-raw": "v1.4.1",
+ "clue/socks-react": "v1.1.0",
+ "clue/stdio-react": "v2.3.0",
+ "clue/term-react": "v1.2.0",
+ "clue/utf8-react": "v1.1.0",
+ "evenement/evenement": "v2.1.0",
+ "guzzlehttp/psr7": "1.6.1",
+ "ipl/html": "v0.3.0",
+ "ipl/orm": "v0.1.0",
+ "ipl/sql": "v0.1.0",
+ "ipl/stdlib": "v0.5.0",
+ "ipl/validator": "v0.1.0",
+ "ipl/web": "v0.1.0",
+ "paragonie/random_compat": "v2.0.18",
+ "predis/predis": "v1.1.6",
+ "psr/http-message": "1.0.1",
+ "ralouphie/getallheaders": "3.0.3",
+ "ramsey/uuid": "3.9.3",
+ "react/cache": "v1.1.0",
+ "react/child-process": "v0.6.1",
+ "react/datagram": "v1.5.0",
+ "react/dns": "v1.4.0",
+ "react/event-loop": "v1.1.1",
+ "react/http": "v1.1.0",
+ "react/http-client": "v0.5.10",
+ "react/promise": "v2.8.0",
+ "react/promise-stream": "v1.2.0",
+ "react/promise-timer": "v1.6.0",
+ "react/socket": "v1.6.0",
+ "react/stream": "v1.1.1",
+ "ringcentral/psr7": "1.3.0",
+ "symfony/polyfill-ctype": "v1.18.1"
+ },
+ "type": "metapackage",
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Thomas Gelf",
+ "email": "thomas@gelf.net"
+ }
+ ],
+ "description": "Provides (replaces) all packages provided by ipl and reactbundle",
+ "support": {
+ "issues": "https://github.com/gipfl/icinga-bundles/issues",
+ "source": "https://github.com/gipfl/icinga-bundles/tree/v0.7.0"
+ },
+ "time": "2020-10-08T17:20:30+00:00"
+ },
+ {
+ "name": "gipfl/icinga-cli-daemon",
+ "version": "v0.3.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/gipfl/icinga-cli-daemon.git",
+ "reference": "19e1b203108fda37de39488fd8ff7ea392a42f03"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/gipfl/icinga-cli-daemon/zipball/19e1b203108fda37de39488fd8ff7ea392a42f03",
+ "reference": "19e1b203108fda37de39488fd8ff7ea392a42f03",
+ "shasum": ""
+ },
+ "require": {
+ "ext-posix": "*",
+ "gipfl/cli": ">=0.5.0",
+ "php": ">=5.6.3",
+ "react/event-loop": ">=1.1",
+ "react/promise": ">=2.7"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "gipfl\\IcingaCliDaemon\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "description": "Helpers for Icinga CLI Daemons",
+ "homepage": "https://github.com/gipfl/icinga-cli-daemon",
+ "support": {
+ "issues": "https://github.com/gipfl/icinga-cli-daemon/issues",
+ "source": "https://github.com/gipfl/icinga-cli-daemon/tree/v0.3.2"
+ },
+ "time": "2021-10-13T08:49:48+00:00"
+ },
+ {
+ "name": "gipfl/icingaweb2",
+ "version": "v0.10.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/gipfl/icingaweb2.git",
+ "reference": "36a2ca4739a0de7ffc365e625bcb81979aca3435"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/gipfl/icingaweb2/zipball/36a2ca4739a0de7ffc365e625bcb81979aca3435",
+ "reference": "36a2ca4739a0de7ffc365e625bcb81979aca3435",
+ "shasum": ""
+ },
+ "require": {
+ "ext-ctype": "*",
+ "gipfl/format": ">=0.3",
+ "gipfl/translation": ">=0.1",
+ "ipl/html": ">=0.2.1",
+ "php": ">=5.6"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "gipfl\\IcingaWeb2\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "description": "Helpers and glue for Icinga Web 2",
+ "homepage": "https://github.com/gipfl/icingaweb2",
+ "support": {
+ "issues": "https://github.com/gipfl/icingaweb2/issues",
+ "source": "https://github.com/gipfl/icingaweb2/tree/v0.10.0"
+ },
+ "time": "2023-01-16T13:50:24+00:00"
+ },
+ {
+ "name": "gipfl/influxdb",
+ "version": "v0.5.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/gipfl/influxdb.git",
+ "reference": "0b7b46ceecb1a6c9fcd738973f773d23d97b374a"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/gipfl/influxdb/zipball/0b7b46ceecb1a6c9fcd738973f773d23d97b374a",
+ "reference": "0b7b46ceecb1a6c9fcd738973f773d23d97b374a",
+ "shasum": ""
+ },
+ "require": {
+ "ext-ctype": "*",
+ "ext-pcntl": "*",
+ "gipfl/curl": ">=0.1.1",
+ "gipfl/json": ">=0.2",
+ "php": ">=5.6.0",
+ "react/event-loop": ">=1.1"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "gipfl\\InfluxDb\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Thomas Gelf",
+ "email": "thomas@gelf.net"
+ }
+ ],
+ "description": "InfluxDB client library",
+ "support": {
+ "issues": "https://github.com/gipfl/influxdb/issues",
+ "source": "https://github.com/gipfl/influxdb/tree/v0.5.0"
+ },
+ "time": "2022-10-07T13:57:22+00:00"
+ },
+ {
+ "name": "gipfl/json",
+ "version": "v0.2.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/gipfl/json.git",
+ "reference": "5635790a19150beeece9edc67e82ecddc52ff4fb"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/gipfl/json/zipball/5635790a19150beeece9edc67e82ecddc52ff4fb",
+ "reference": "5635790a19150beeece9edc67e82ecddc52ff4fb",
+ "shasum": ""
+ },
+ "require": {
+ "ext-json": "*"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "gipfl\\Json\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Thomas Gelf",
+ "email": "thomas@gelf.net"
+ }
+ ],
+ "description": "Simple JSON-related helper classes and interfaces",
+ "support": {
+ "issues": "https://github.com/gipfl/json/issues",
+ "source": "https://github.com/gipfl/json/tree/v0.2.0"
+ },
+ "time": "2021-11-18T12:48:06+00:00"
+ },
+ {
+ "name": "gipfl/linux-health",
+ "version": "v0.2.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/gipfl/linux-health.git",
+ "reference": "4d212535565f353f88c9eef3ecb54e20c40b8b37"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/gipfl/linux-health/zipball/4d212535565f353f88c9eef3ecb54e20c40b8b37",
+ "reference": "4d212535565f353f88c9eef3ecb54e20c40b8b37",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.4.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "gipfl\\LinuxHealth\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Thomas Gelf",
+ "email": "thomas@gelf.net"
+ }
+ ],
+ "description": "Various little Linux Health based classes",
+ "support": {
+ "issues": "https://github.com/gipfl/linux-health/issues",
+ "source": "https://github.com/gipfl/linux-health/tree/master"
+ },
+ "time": "2020-07-30T09:11:27+00:00"
+ },
+ {
+ "name": "gipfl/log",
+ "version": "v0.7.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/gipfl/log.git",
+ "reference": "df770bdc9c09b0428831345af7ce7d45a0c3a1bb"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/gipfl/log/zipball/df770bdc9c09b0428831345af7ce7d45a0c3a1bb",
+ "reference": "df770bdc9c09b0428831345af7ce7d45a0c3a1bb",
+ "shasum": ""
+ },
+ "require": {
+ "ext-iconv": "*",
+ "php": ">=5.6.0",
+ "psr/log": "^1"
+ },
+ "require-dev": {
+ "gipfl/protocol-jsonrpc": ">=0.2",
+ "gipfl/systemd": ">=0.3"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "gipfl\\Log\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Thomas Gelf",
+ "email": "thomas@gelf.net"
+ }
+ ],
+ "description": "Lightweight PSR-3 compatible logger",
+ "support": {
+ "issues": "https://github.com/gipfl/log/issues",
+ "source": "https://github.com/gipfl/log/tree/v0.7.0"
+ },
+ "time": "2021-11-11T15:39:23+00:00"
+ },
+ {
+ "name": "gipfl/openrpc",
+ "version": "v0.2.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/gipfl/openrpc.git",
+ "reference": "a29b8da2cdaf7d757d86aa0fe6d719cf68fb95ad"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/gipfl/openrpc/zipball/a29b8da2cdaf7d757d86aa0fe6d719cf68fb95ad",
+ "reference": "a29b8da2cdaf7d757d86aa0fe6d719cf68fb95ad",
+ "shasum": ""
+ },
+ "require": {
+ "ext-json": "*",
+ "php": ">=5.6.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "gipfl\\OpenRpc\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Thomas Gelf",
+ "email": "thomas@gelf.net"
+ }
+ ],
+ "description": "OpenRPC Connection implementation",
+ "support": {
+ "issues": "https://github.com/gipfl/openrpc/issues",
+ "source": "https://github.com/gipfl/openrpc/tree/v0.2.1"
+ },
+ "time": "2021-11-29T13:53:50+00:00"
+ },
+ {
+ "name": "gipfl/process",
+ "version": "v0.4.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/gipfl/process.git",
+ "reference": "ddbc3971cf56b35ce17d7cc8aaf241e87a792938"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/gipfl/process/zipball/ddbc3971cf56b35ce17d7cc8aaf241e87a792938",
+ "reference": "ddbc3971cf56b35ce17d7cc8aaf241e87a792938",
+ "shasum": ""
+ },
+ "require": {
+ "ext-json": "*",
+ "gipfl/json": ">=0.1",
+ "gipfl/linux-health": ">=0.2",
+ "php": ">=5.6.0",
+ "react/child-process": ">=0.6",
+ "react/promise": "^2"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "gipfl\\Process\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Thomas Gelf",
+ "email": "thomas@gelf.net"
+ }
+ ],
+ "description": "Process-related utility classes",
+ "support": {
+ "issues": "https://github.com/gipfl/process/issues",
+ "source": "https://github.com/gipfl/process/tree/v0.4.0"
+ },
+ "time": "2022-09-02T09:03:58+00:00"
+ },
+ {
+ "name": "gipfl/protocol",
+ "version": "v0.2.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/gipfl/protocol.git",
+ "reference": "bf1a0e160fe6f74c6be60be569c466d57551e73d"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/gipfl/protocol/zipball/bf1a0e160fe6f74c6be60be569c466d57551e73d",
+ "reference": "bf1a0e160fe6f74c6be60be569c466d57551e73d",
+ "shasum": ""
+ },
+ "require": {
+ "evenement/evenement": "^2",
+ "php": ">=5.4.0",
+ "react/stream": "^1.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "gipfl\\Protocol\\Generic\\": "src/Generic",
+ "gipfl\\Protocol\\Exception\\": "src/Exception"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Thomas Gelf",
+ "email": "thomas@gelf.net"
+ }
+ ],
+ "description": "Base library for some network protocol implementations",
+ "support": {
+ "issues": "https://github.com/gipfl/protocol/issues",
+ "source": "https://github.com/gipfl/protocol/tree/v0.2.0"
+ },
+ "time": "2020-09-22T04:22:23+00:00"
+ },
+ {
+ "name": "gipfl/protocol-jsonrpc",
+ "version": "v0.11.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/gipfl/protocol-jsonrpc.git",
+ "reference": "30b0241c67019746fce0b464543d8f936bdb9b68"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/gipfl/protocol-jsonrpc/zipball/30b0241c67019746fce0b464543d8f936bdb9b68",
+ "reference": "30b0241c67019746fce0b464543d8f936bdb9b68",
+ "shasum": ""
+ },
+ "require": {
+ "ext-json": "*",
+ "gipfl/json": ">=0.1",
+ "gipfl/openrpc": "^0.2.1",
+ "gipfl/protocol": ">=0.2",
+ "php": ">=5.6.0",
+ "psr/log": ">=1.1",
+ "react/promise": ">=2.7",
+ "react/stream": ">=1.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^9.3 || ^7.5 || ^6.5 || ^5.7",
+ "squizlabs/php_codesniffer": "^3.6"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "gipfl\\Protocol\\JsonRpc\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Thomas Gelf",
+ "email": "thomas@gelf.net"
+ }
+ ],
+ "description": "JsonRPC Connection implementation",
+ "support": {
+ "issues": "https://github.com/gipfl/protocol-jsonrpc/issues",
+ "source": "https://github.com/gipfl/protocol-jsonrpc/tree/v0.11.0"
+ },
+ "time": "2023-01-16T13:58:38+00:00"
+ },
+ {
+ "name": "gipfl/protocol-netstring",
+ "version": "v0.1.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/gipfl/protocol-netstring.git",
+ "reference": "c0021ea9d69883760ac176121041a05c9a51ca11"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/gipfl/protocol-netstring/zipball/c0021ea9d69883760ac176121041a05c9a51ca11",
+ "reference": "c0021ea9d69883760ac176121041a05c9a51ca11",
+ "shasum": ""
+ },
+ "require": {
+ "ext-ctype": "*",
+ "gipfl/protocol": ">=0.2",
+ "php": ">=5.4.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "gipfl\\Protocol\\NetString\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Thomas Gelf",
+ "email": "thomas@gelf.net"
+ }
+ ],
+ "description": "Simple NetString stream wrapper",
+ "support": {
+ "issues": "https://github.com/gipfl/protocol-netstring/issues",
+ "source": "https://github.com/gipfl/protocol-netstring/tree/v0.1.1"
+ },
+ "time": "2020-10-08T23:38:20+00:00"
+ },
+ {
+ "name": "gipfl/react-utils",
+ "version": "v0.3.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/gipfl/react-utils.git",
+ "reference": "37ff2daa4bcc36aced4cc9df5198caad519d7bdb"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/gipfl/react-utils/zipball/37ff2daa4bcc36aced4cc9df5198caad519d7bdb",
+ "reference": "37ff2daa4bcc36aced4cc9df5198caad519d7bdb",
+ "shasum": ""
+ },
+ "require": {
+ "gipfl/log": ">=0.1",
+ "php": ">=5.6.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "gipfl\\ReactUtils\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Thomas Gelf",
+ "email": "thomas@gelf.net"
+ }
+ ],
+ "description": "Useful ReactPHP-related helper classes and methods",
+ "support": {
+ "issues": "https://github.com/gipfl/react-utils/issues",
+ "source": "https://github.com/gipfl/react-utils/tree/v0.3.0"
+ },
+ "time": "2021-10-29T12:17:04+00:00"
+ },
+ {
+ "name": "gipfl/simple-daemon",
+ "version": "v0.6.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/gipfl/simple-daemon.git",
+ "reference": "b7cd3be54c171d20c6692dbb331a3ae6fe90f276"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/gipfl/simple-daemon/zipball/b7cd3be54c171d20c6692dbb331a3ae6fe90f276",
+ "reference": "b7cd3be54c171d20c6692dbb331a3ae6fe90f276",
+ "shasum": ""
+ },
+ "require": {
+ "evenement/evenement": "*",
+ "ext-pcntl": "*",
+ "gipfl/cli": ">=0.5",
+ "gipfl/json": ">=0.1",
+ "gipfl/systemd": ">=0.3",
+ "php": ">=5.6.0",
+ "psr/log": ">=1.0",
+ "react/event-loop": ">=1.1",
+ "react/promise": "^2",
+ "react/promise-timer": ">=1.5"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "gipfl\\SimpleDaemon\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Thomas Gelf",
+ "email": "thomas@gelf.net"
+ }
+ ],
+ "description": "Run a simple daemon",
+ "support": {
+ "issues": "https://github.com/gipfl/simple-daemon/issues",
+ "source": "https://github.com/gipfl/simple-daemon/tree/v0.6.0"
+ },
+ "time": "2022-10-07T14:08:26+00:00"
+ },
+ {
+ "name": "gipfl/socket",
+ "version": "v0.4.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/gipfl/socket.git",
+ "reference": "1a2c78709e0bcf7de2469860fbfa807a4bcee555"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/gipfl/socket/zipball/1a2c78709e0bcf7de2469860fbfa807a4bcee555",
+ "reference": "1a2c78709e0bcf7de2469860fbfa807a4bcee555",
+ "shasum": ""
+ },
+ "require": {
+ "evenement/evenement": ">=2.0",
+ "ext-posix": "*",
+ "ext-sockets": "*",
+ "gipfl/json": ">=0.1",
+ "php": ">=5.6.0",
+ "react/event-loop": ">=1.0",
+ "react/socket": ">=1.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "gipfl\\Socket\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Thomas Gelf",
+ "email": "thomas@gelf.net"
+ }
+ ],
+ "description": "Helpful ReactPHP socket utility classes",
+ "support": {
+ "issues": "https://github.com/gipfl/socket/issues",
+ "source": "https://github.com/gipfl/socket/tree/v0.4.0"
+ },
+ "time": "2021-12-02T02:23:18+00:00"
+ },
+ {
+ "name": "gipfl/stream",
+ "version": "v0.2.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/gipfl/stream.git",
+ "reference": "ae0f8f4fd47a6dcc57885b232e239b0ba82889a3"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/gipfl/stream/zipball/ae0f8f4fd47a6dcc57885b232e239b0ba82889a3",
+ "reference": "ae0f8f4fd47a6dcc57885b232e239b0ba82889a3",
+ "shasum": ""
+ },
+ "require": {
+ "react/event-loop": ">=1.0",
+ "react/stream": ">=1.0"
+ },
+ "require-dev": {
+ "gipfl/test": ">=0.1.1",
+ "phpunit/phpunit": "^9.3 || ^7.5 || ^6.5 || ^5.7",
+ "squizlabs/php_codesniffer": "^3.6"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "gipfl\\Stream\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Thomas Gelf",
+ "email": "thomas@gelf.net"
+ }
+ ],
+ "description": "Helpful ReactPHP stream utility classes",
+ "support": {
+ "issues": "https://github.com/gipfl/stream/issues",
+ "source": "https://github.com/gipfl/stream/tree/v0.2.0"
+ },
+ "time": "2021-11-27T12:17:36+00:00"
+ },
+ {
+ "name": "gipfl/systemd",
+ "version": "v0.3.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/gipfl/systemd.git",
+ "reference": "ccfabb352a9ea9fd33d4cb426bc3793a651bf466"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/gipfl/systemd/zipball/ccfabb352a9ea9fd33d4cb426bc3793a651bf466",
+ "reference": "ccfabb352a9ea9fd33d4cb426bc3793a651bf466",
+ "shasum": ""
+ },
+ "require": {
+ "ext-posix": "*",
+ "ext-sockets": "*",
+ "php": ">=5.6.3",
+ "react/event-loop": "^1.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "gipfl\\SystemD\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Thomas Gelf",
+ "email": "thomas@gelf.net"
+ }
+ ],
+ "description": "SystemD-related library",
+ "support": {
+ "issues": "https://github.com/gipfl/systemd/issues",
+ "source": "https://github.com/gipfl/systemd/tree/v0.3.0"
+ },
+ "time": "2020-11-13T22:35:11+00:00"
+ },
+ {
+ "name": "gipfl/translation",
+ "version": "v0.1.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/gipfl/translation.git",
+ "reference": "e974e6a2f0b524678b6554ee0046b5dd5f70a25d"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/gipfl/translation/zipball/e974e6a2f0b524678b6554ee0046b5dd5f70a25d",
+ "reference": "e974e6a2f0b524678b6554ee0046b5dd5f70a25d",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.4.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "gipfl\\Translation\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Thomas Gelf",
+ "email": "thomas@gelf.net"
+ }
+ ],
+ "description": "Translation helpers",
+ "support": {
+ "issues": "https://github.com/gipfl/translation/issues",
+ "source": "https://github.com/gipfl/translation/tree/master"
+ },
+ "time": "2019-09-17T20:36:20+00:00"
+ },
+ {
+ "name": "gipfl/web",
+ "version": "v0.11.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/gipfl/web.git",
+ "reference": "d331fe195f7bab08561ee015ba56edeb334a1533"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/gipfl/web/zipball/d331fe195f7bab08561ee015ba56edeb334a1533",
+ "reference": "d331fe195f7bab08561ee015ba56edeb334a1533",
+ "shasum": ""
+ },
+ "require": {
+ "gipfl/translation": ">=0.1.1",
+ "ipl/html": ">=0.3",
+ "php": ">=5.6.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "gipfl\\Web\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Thomas Gelf",
+ "email": "thomas@gelf.net"
+ }
+ ],
+ "description": "Various web widgets",
+ "support": {
+ "issues": "https://github.com/gipfl/web/issues",
+ "source": "https://github.com/gipfl/web/tree/v0.11.0"
+ },
+ "time": "2022-09-01T07:25:09+00:00"
+ },
+ {
+ "name": "gipfl/zfdb",
+ "version": "v0.4.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/gipfl/zfdb.git",
+ "reference": "beb7ceabc80e8c81f0e237afcfb064a67e1432d6"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/gipfl/zfdb/zipball/beb7ceabc80e8c81f0e237afcfb064a67e1432d6",
+ "reference": "beb7ceabc80e8c81f0e237afcfb064a67e1432d6",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.4"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "gipfl\\ZfDb\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "description": "Zend_Db from Zend Framework 1. For compatibility reasons only",
+ "support": {
+ "issues": "https://github.com/gipfl/zfdb/issues",
+ "source": "https://github.com/gipfl/zfdb/tree/v0.4.0"
+ },
+ "time": "2022-08-30T13:10:26+00:00"
+ },
+ {
+ "name": "gipfl/zfdbstore",
+ "version": "v0.3.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/gipfl/zfdbstore.git",
+ "reference": "5296226865a0c053f29f57a1bccd50b17cab5248"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/gipfl/zfdbstore/zipball/5296226865a0c053f29f57a1bccd50b17cab5248",
+ "reference": "5296226865a0c053f29f57a1bccd50b17cab5248",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.6"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "gipfl\\ZfDbStore\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "description": "Storable class helpers for ZfDb",
+ "support": {
+ "issues": "https://github.com/gipfl/zfdbstore/issues",
+ "source": "https://github.com/gipfl/zfdbstore/tree/v0.3.0"
+ },
+ "time": "2023-01-16T13:54:52+00:00"
+ },
+ {
+ "name": "psr/log",
+ "version": "1.1.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-fig/log.git",
+ "reference": "d49695b909c3b7628b6289db5479a1c204601f11"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-fig/log/zipball/d49695b909c3b7628b6289db5479a1c204601f11",
+ "reference": "d49695b909c3b7628b6289db5479a1c204601f11",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.1.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Psr\\Log\\": "Psr/Log/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "PHP-FIG",
+ "homepage": "https://www.php-fig.org/"
+ }
+ ],
+ "description": "Common interface for logging libraries",
+ "homepage": "https://github.com/php-fig/log",
+ "keywords": [
+ "log",
+ "psr",
+ "psr-3"
+ ],
+ "support": {
+ "source": "https://github.com/php-fig/log/tree/1.1.4"
+ },
+ "time": "2021-05-03T11:20:27+00:00"
+ }
+ ],
+ "packages-dev": [],
+ "aliases": [],
+ "minimum-stability": "stable",
+ "stability-flags": [],
+ "prefer-stable": false,
+ "prefer-lowest": false,
+ "platform": {
+ "php": ">=5.6.3",
+ "ext-ctype": "*"
+ },
+ "platform-dev": [],
+ "platform-overrides": {
+ "php": "5.6.3"
+ },
+ "plugin-api-version": "2.1.0"
+}
diff --git a/configuration.php b/configuration.php
new file mode 100644
index 0000000..0e23fb5
--- /dev/null
+++ b/configuration.php
@@ -0,0 +1,4 @@
+<?php
+
+$this->provideJsFile('loader.js');
+$this->provideJsFile('combined.js');
diff --git a/extendStyles.php b/extendStyles.php
new file mode 100644
index 0000000..7082188
--- /dev/null
+++ b/extendStyles.php
@@ -0,0 +1,21 @@
+<?php /** @noinspection PhpMissingParentConstructorInspection */
+
+namespace Icinga\Module\Incubator;
+
+use Icinga\Application\Icinga;
+use Icinga\Web\StyleSheet;
+
+class Styles extends StyleSheet
+{
+ public function __construct()
+ {
+ $pubPath = Icinga::app()->getBaseDir('public');
+ $slashes = \substr_count($pubPath, '/');
+ $prefix = \str_repeat('/..', $slashes) . __DIR__;
+ $files = ["$prefix/public/css/combined.less"];
+
+ foreach ($files as $file) {
+ self::$lessFiles[] = $file;
+ }
+ }
+}
diff --git a/module.info b/module.info
new file mode 100644
index 0000000..6a78bbb
--- /dev/null
+++ b/module.info
@@ -0,0 +1,6 @@
+Name: Incubator
+Version: 0.20.0
+Depends: ipl (>=0.5.0), reactbundle (>=0.8.0)
+Description: Incubator provides bleeding-edge libraries
+ This repository ships libraries useful for Icinga Web 2 modules. Please download
+ the latest release and install it like any other module.
diff --git a/public/css/combined.less b/public/css/combined.less
new file mode 100644
index 0000000..422a94c
--- /dev/null
+++ b/public/css/combined.less
@@ -0,0 +1,969 @@
+@color-diff-ins: @color-ok;
+@color-diff-del: @color-critical;
+@color-diff-changed-old: fade(@color-critical, 30%);
+@color-diff-changed-new: fade(@color-ok, 30%);
+@color-diff-green-shade-light: #dfd;
+@color-diff-pale-green: #9e9;
+@color-diff-text-on-diff: #051030;
+@color-change-replace-del: #e99;
+
+.Differences {
+ width: 100%;
+ table-layout: fixed;
+ empty-cells: show;
+}
+
+.Differences thead {
+ display: none;
+}
+
+.Differences thead th {
+ text-align: left;
+ padding-left: 4 / 14 * 16em;
+}
+
+.Differences tbody th {
+ text-align: right;
+ width: 4em;
+ padding: 1px 2px;
+ border-right: 1px solid @gray-light;
+ background: @gray-lightest;
+ font-weight: normal;
+ vertical-align: top;
+}
+
+.Differences tbody td {
+ color: @text-color;
+ width: 50%;
+ .preformatted();
+ word-break: break-all;
+}
+
+.DifferencesSideBySide {
+ ins, del {
+ text-decoration: none;
+ }
+
+ .ChangeInsert {
+ td.Left {
+ background: @gray-lighter;
+ }
+ td.Right {
+ background: @color-diff-changed-new;
+ color: @color-diff-text-on-diff;
+ }
+ }
+
+ .ChangeDelete {
+ td.Left {
+ background: @color-diff-changed-old;
+ color: @color-diff-text-on-diff;
+ }
+ td.Right {
+ background: @gray-lighter;
+ }
+ }
+
+ .ChangeReplace {
+ td.Left {
+ background: @color-diff-changed-old;
+ color: @color-diff-text-on-diff;
+ del {
+ background: @color-diff-del;
+ }
+ }
+
+ td.Right {
+ background: @color-diff-changed-new;
+ color: @color-diff-text-on-diff;
+ ins {
+ background: @color-diff-ins;
+ }
+ }
+
+ }
+}
+
+.Differences .Skipped {
+ background: @gray-lightest;
+}
+
+.DifferencesInline .ChangeReplace .Left,
+.DifferencesInline .ChangeDelete .Left {
+ background: @color-diff-changed-old;
+}
+
+.DifferencesInline .ChangeReplace .Right,
+.DifferencesInline .ChangeInsert .Right {
+ background: @color-diff-green-shade-light;
+}
+
+.DifferencesInline .ChangeReplace ins {
+ background: @color-diff-pale-green;
+}
+
+.DifferencesInline .ChangeReplace del {
+ background: @color-change-replace-del;
+}
+
+.DifferencesInline {
+ tr td:last-child {
+ width: 90%;
+ }
+ tr th:first-child {
+ width: 5%;
+ }
+ tr th:nth-child(2) {
+ width: 5%;
+ }
+ ins, del {
+ text-decoration: none;
+ }
+}
+
+#layout.compact-layout, #layout.default-layout {
+ &.twocols table.Differences {
+ th {
+ font-size: 0.75em;
+ }
+ td {
+ font-size: 0.916em;
+ }
+ }
+}
+div.gipfl-action-bar {
+ > * {
+ vertical-align: middle;
+ }
+ .pagination-control {
+ float: none;
+ clear: none;
+ display: inline-block;
+ line-height: inherit;
+ margin: 0;
+ }
+
+ form input {
+ margin: 0;
+ }
+}
+
+div.gipfl-action-bar a, div.gipfl-action-bar form i {
+ color: @icinga-blue;
+}
+
+div.gipfl-action-bar a.badge {
+ color: inherit;
+}
+
+div.gipfl-action-bar > a {
+ margin-right: 1em;
+}
+
+#layout.twocols div.gipfl-action-bar .pagination-control {
+ li {
+ display: none;
+ }
+
+ li:nth-child(1), li.active, li:last-child {
+ display: list-item;
+ }
+
+ li.active a {
+ border-bottom: none;
+ }
+}
+
+/** Dropdown in action bar **/
+div.gipfl-action-bar ul {
+ padding: 0;
+ margin: 0;
+
+ li {
+ list-style-type: none;
+ a { display: block; }
+ }
+}
+
+div.gipfl-action-bar > ul {
+ display: inline-block;
+ > li {
+ display: inline-block;
+ }
+ ul {
+ padding: 0.5em 1em 0 0;
+ min-width: 10em;
+ position: absolute;
+ display: none;
+ background-color: @menu-flyout-bg-color;
+ border: 1px solid @gray-light;
+ box-shadow: 0.3em 0.3em 0.3em 0 rgba(0, 0, 0, 0.2);
+
+ a {
+ display: block;
+ padding: 0.3em 0.5em 0.3em 1em;
+ margin: 0;
+ outline: none;
+ color: @menu-flyout-color;
+
+ &:hover {
+ text-decoration: underline;
+ &:before {
+ text-decoration: none;
+ }
+ }
+ }
+
+ li.active a {
+ font-weight: bold;
+ }
+ }
+ > li:hover ul {
+ display: block;
+ z-index: 2;
+ }
+ > li > a {
+ padding: 0.2em 0.5em;
+ }
+ > li:hover {
+ background-color: @tr-active-color;
+ border-top-left-radius: 0.1em;
+ border-top-right-radius: 0.1em;
+ & > a {
+ color: @icinga-blue;
+ text-decoration: none;
+ }
+ }
+}
+/** END of dropdown in action bar **/
+form.gipfl-quicksearch {
+ display: block;
+ input.search {
+ width: 8em;
+ min-width: unset;
+ border: none;
+ background-color: inherit;
+ padding-left: 2em;
+ margin-left: 1.5em;
+ font-size: 0.75em;
+ font-weight: normal;
+ &:focus {
+ width: 16em;
+ border: none;
+ }
+ }
+}
+@gipfl-color-low-sat-blue: #dae3e6;
+// map Icinga vars, to avoid warnings in other files
+@gipfl-color-main: @icinga-blue;
+@gipfl-color-text: @text-color;
+@gipfl-color-gray-light: @gray-light;
+@gipfl-bg-element: #f2f1f0;
+@gipfl-bg-element: @low-sat-blue;
+@gipfl-icon-font: 'ifont';
+/**
+ * Lightweight support for small collapsible components
+ *
+ * @since v0.6.0
+ */
+.gipfl-collapsible-control {
+ display: block;
+ border-bottom: 1px solid @gipfl-color-gray-light;
+ &:hover {
+ cursor: pointer;
+ text-decoration: none;
+ }
+ &::after {
+ font-family: @gipfl-icon-font;
+ content: '\f103';
+ float: right;
+ margin-right: 0.5em;
+ color: @gipfl-color-gray-light;
+ }
+ &:hover {
+ &::after {
+ color: @gipfl-color-text;
+ }
+ }
+ &:focus {
+ text-decoration: none;
+ &::after {
+ color: @gipfl-color-main;
+ }
+ }
+}
+
+.gipfl-collapsible .collapsed {
+ :not(.gipfl-collapsible-control) {
+ display: none;
+ }
+ .gipfl-collapsible-control {
+ &::after {
+ content: '\e87a';
+ }
+ }
+}
+
+ul.gipfl-collapsible {
+ list-style-type: none;
+ margin: 0;
+ padding: 0;
+ li {
+ margin: 0;
+ }
+ .gipfl-collapsible-control {
+ font-weight: bold;
+ line-height: 2em;
+ }
+}
+/**
+ * State Hints
+ *
+ * @since v0.6.0
+ */
+div.gipfl-widget-hint {
+ border: 1px solid @text-color;
+ padding: 0.5em;
+ line-height: 2em;
+ max-width: 60em;
+ border-left-width: 3em;
+ border-radius: 0.25em;
+ margin-bottom: 1em;
+ background-color: @body-bg-color;
+ box-shadow: fade(@text-color, 30%) 1em 0 1.5em 0;
+ &:before {
+ position: relative;
+ margin-left: -1.4em;
+ margin-right: 0.5em;
+ height: 100%;
+ vertical-align: middle;
+ font-family: 'ifont';
+ color: white;
+ font-size: 2em;
+ }
+ &.ok {
+ border-color: @color-ok;
+ box-shadow: fade(@color-ok, 30%) 1em 0 1.5em 0;
+ }
+ &.info {
+ border-color: @color-pending;
+ box-shadow: fade(@color-pending, 30%) 1em 0 1.5em 0;
+ }
+ &.warning {
+ border-color: @color-warning;
+ box-shadow: fade(@color-warning, 30%) 1em 0 1.5em 0;
+ }
+ &.error {
+ border-color: @color-critical;
+ box-shadow: fade(@color-critical, 30%) 1em 0 1.5em 0;
+ }
+ &.critical:before, &.error:before {
+ content: '\e881';
+ }
+ &.warning:before {
+ content: '\e881';
+ }
+ &.info:before {
+ content: '\e87d';
+ }
+ &.ok:before {
+ content: '\e803';
+ }
+ a {
+ text-decoration: underline;
+ }
+}
+table.gipfl-name-value-table {
+ width: 100%;
+ > tbody > tr > th {
+ color: @text-color-light;
+ // Reset default font-weight
+ font-weight: bold;
+ padding-left: 0;
+ text-align: right;
+ vertical-align: top;
+ width: 12em;
+ }
+ > tbody > tr > td {
+ vertical-align: top;
+ }
+}
+
+table.gipfl-name-value-table a {
+ color: @icinga-blue;
+}
+
+table.gipfl-name-value-table pre {
+ background-color: transparent;
+ margin: 0;
+ padding: 0;
+ font-size: unset;
+ display: inline;
+}
+form.gipfl-form {
+ input[type="submit"] {
+ margin-right: 0.5em;
+ }
+ input[type="submit"]:first-of-type {
+ background-color: @icinga-blue;
+ color: @text-color-inverted;
+ border: 2px solid @icinga-blue;
+ font-weight: bold;
+ }
+
+ select:not([multiple]) {
+ padding-right: 1.5625em;
+ background-image: url(../img/select-icon.svg);
+ background-repeat: no-repeat;
+ background-position: right center;
+ background-size: contain;
+ }
+
+ input.validated {
+ background-color: fade(@color-ok, 30%);
+ border-color: @color-ok;
+ }
+
+ :not(output):-moz-ui-invalid, :not(output).invalid {
+ background-color: fade(@color-critical, 30%);
+ border-color: @color-critical;
+ box-shadow: none;
+ }
+
+ input[type=text].input-with-button {
+ max-width: 30em;
+ min-width: 18em;
+ width: 80%;
+ margin: 0;
+ border-top-right-radius: unset;
+ border-bottom-right-radius: unset;
+ border-right-style: none;
+ &:focus {
+ border-right-style: none;
+ }
+ }
+
+ input[type=submit].input-element-related-button {
+ width: 20%;
+ max-width: 6em;
+ background-color: @icinga-blue;
+ color: @text-color-inverted;
+ border: 1px solid @icinga-blue;
+ border-radius: 0 3px 3px 0;
+ -moz-border-radius: 0 3px 3px 0;
+ -webkit-border-radius: 0 3px 3px 0;
+ line-height: 2em;
+ height: 2.4em;
+ padding: 0;
+ margin: 0 1em 0 0;
+ &:hover {
+ background-color: @text-color-inverted;
+ color: @icinga-blue;
+ border-color: @icinga-blue;
+ }
+ }
+ p.gipfl-widget-hint {
+ max-width: 52.5em;
+ }
+ p.gipfl-element-description {
+ max-width: 36em;
+ }
+}
+form.gipfl-form {
+ label {
+ line-height: 2em;
+ }
+ dl {
+ margin: 0;
+ padding: 0;
+ &.active {
+ dt label {
+ text-decoration: underline;
+ }
+ }
+ }
+
+ dt {
+ padding: 0;
+ margin: 0.5em 0 0 0;
+ display: inline-block;
+ vertical-align: top;
+ min-width: 8em;
+ max-width: 16em;
+ min-height: 2.5em;
+ width: 27%;
+ label {
+ color: @text-color;
+ font-weight: bold;
+ width: 12em;
+ font-size: inherit;
+
+ &:hover {
+ text-decoration: underline;
+ cursor: pointer;
+ }
+ }
+
+ &.errors label {
+ color: @color-critical;
+ }
+ }
+
+ dd {
+ padding: 0.3em 0.5em;
+ margin: 0;
+ display: inline-block;
+ width: 73%;
+ min-height: 2.5em;
+ vertical-align: top;
+
+ &.errors {
+ input[type=text], select {
+ border-color: @color-critical;
+ }
+ }
+
+ &.full-width {
+ padding: 0.5em;
+ width: 100%;
+ }
+
+ &:after {
+ display: block;
+ content: '';
+ }
+
+ ul.errors, ul.gipfl-form-element-errors {
+ list-style-type: none;
+ padding-left: 0.3em;
+
+ li {
+ color: @colorCritical;
+ padding: 0.3em;
+ }
+ }
+ }
+
+ input[type="text"],
+ input[type="password"],
+ input[type="number"],
+ input[type="datetime-local"],
+ input[type="date"],
+ input[type="time"],
+ textarea,
+ select {
+ background-color: @gipfl-bg-element;
+ }
+
+ .errors label {
+ color: @color-critical;
+ }
+
+ textarea {
+ height: auto;
+ max-width: 100%;
+ }
+
+ input[type=file] {
+ background-color: @text-color-inverted;
+ padding-right: 1em;
+ }
+
+ input[type=submit] {
+ .button();
+ transition: none; // Avoid flickering on autosubmit
+ border-width: 1px;
+ margin-top: 0.5em;
+
+ &:disabled {
+ border-color: @gray-light;
+ // background-color: @gray-light;
+ // color: #fff;
+ cursor: wait;
+ }
+ &:first-of-type {
+ border-width: 2px;
+ }
+ }
+
+ select {
+ border: 1px solid #ddd;
+ cursor: pointer;
+ }
+
+ input,
+ select,
+ select option,
+ textarea {
+ -webkit-appearance: none;
+ -moz-appearance: none;
+ }
+
+ select::-ms-expand,
+ input::-ms-expand,
+ textarea::-ms-expand { // for IE 11
+ display: none;
+ }
+
+ input[type=submit].link-button {
+ color: @icinga-blue;
+ background: none;
+ border: none;
+ font-weight: normal;
+ padding: 0;
+ margin: 0;
+ text-align: left;
+
+ &:hover {
+ text-decoration: underline;
+ }
+ }
+
+ ul.form-errors {
+ list-style-type: none;
+ margin-bottom: 0.5em;
+ padding: 0;
+
+ ul.errors {
+ list-style-type: none;
+ padding: 0;
+ }
+
+ ul.errors li {
+ background: @color-critical;
+ font-weight: bold;
+ padding: 0.5em 1em;
+ color: white;
+ }
+ }
+
+ input[type=text], input[type=password], textarea, select {
+ max-width: 36em;
+ min-width: 20em;
+ width: 100%;
+ }
+ input:not([type=submit]),
+ textarea,
+ select {
+ line-height: 2em;
+ height: 2.4em;
+ padding-left: 0.5em;
+ border-style: solid;
+ border-color: @gray-lighter;
+ border-width: 1px;
+ border-radius: 3px;
+ color: @text-color;
+
+ &:hover {
+ border-color: @gray;
+ }
+
+ &:focus, &:focus:hover {
+ border-color: @icinga-blue;
+ outline: none;
+ }
+ }
+ // duplicated from 40 -> ordering problem
+ input.validated {
+ background-color: fade(@color-ok, 30%);
+ border-color: @color-ok;
+ }
+ textarea {
+ height: unset;
+ min-height: 2.4em;
+ resize: vertical;
+ }
+
+ input.search {
+ background-image: url("../img/icons/search.png");
+ background-repeat: no-repeat;
+ padding-left: 2em;
+ }
+
+ select[multiple], select[multiple=multiple] {
+ height: auto;
+ }
+
+ select option {
+ height: 2em;
+ padding-top: 0.3em;
+ }
+
+ #_FAKE_SUBMIT {
+ position: absolute;
+ left: -100%;
+ }
+
+ select::-moz-focus-inner {
+ border: 0;
+ }
+
+ select:-moz-focusring {
+ color: transparent;
+ text-shadow: 0 0 0 #000;
+ }
+
+ select[value=""] {
+ color: blue;
+ border: 1px solid #666;
+ // background-color: white;
+ }
+
+ select option {
+ color: inherit;
+ padding-left: 0.5em;
+ }
+
+ // default option
+ select option[value=""] {
+ // color: #aaa;
+ }
+
+ fieldset {
+ margin: 0;
+ min-width: 36em;
+
+ padding: 0 0 1.5em 0;
+ border: none;
+
+ legend {
+ margin: 0 0 0.5em 0;
+ font-size: 1em;
+ border-bottom: 1px solid @gray-light;
+ font-weight: bold;
+ display: block;
+ width: 100%;
+ padding-left: 1em;
+ line-height: 2em;
+ cursor: pointer;
+ user-select: none;
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+
+ &:hover {
+ border-color: @text-color;
+ }
+
+ &::before {
+ // icon: down-dir
+ font-family: 'ifont';
+ content: '\e81d';
+ margin-left: -1em;
+ padding-top: 0;
+ float: left;
+ color: inherit;
+ }
+ }
+
+ &.collapsed {
+ dl, dd, dt, ul, div {
+ display: none;
+ }
+
+ legend {
+ margin: 0;
+ }
+ legend::before {
+ // icon: right-dir
+ content: '\e820';
+ }
+
+ margin-bottom: 0.2em;
+ padding-bottom: 0;
+ }
+ }
+}
+
+#layout.minimal-layout div.content form.gipfl-form {
+ dt, dd {
+ display: block;
+ width: auto;
+ }
+
+ dt label {
+ color: @text-color;
+ }
+
+ fieldset {
+ min-width: unset;
+ }
+
+ input[type=text], input[type=password], textarea, select {
+ max-width: unset;
+ min-width: unset;
+ border-color: @gray-light;
+ }
+
+ dd.active {
+ input[type=text], input[type=password], textarea, select {
+ border-color: @icinga-blue;
+ }
+ }
+
+ fieldset.collapsed {
+ dd, dt, ul, div {
+ display: none;
+ }
+ }
+}
+
+#layout.minimal-layout div.content form.gipfl-form {
+ ul.extensible-set {
+ max-width: unset;
+ border: 1px solid @gray-light;
+ }
+
+ dd.active ul.extensible-set {
+ border: 1px solid @icinga-blue;
+
+ input[type=submit]:first-of-type {
+ border-width: 1px;
+ }
+ }
+
+ dd input.related-action[type='submit'] {
+ display: none;
+ }
+
+ dd.active li.active input.related-action[type='submit'] {
+ display: inline-block;
+ }
+
+ dd.active ul.extensible-set, ul.extensible-set.sortable {
+ input[type=text], select {
+ width: 100%;
+ }
+
+ input[type=text] {
+ // background-color: white;
+ border: 1px solid white;
+ }
+ }
+}
+form.gipfl-inline-form {
+ display: inline-block;
+ select {
+ width: auto;
+ min-width: unset;
+ max-width: unset;
+ border: none;
+ padding: 0 0.5em;
+ margin: 0;
+ line-height: unset;
+ box-sizing:border-box;
+ height: 1.5em;
+
+ &:hover {
+ border: none;
+ }
+
+ &:focus {
+ border: none;
+ }
+ }
+ input[type=submit] {
+ padding: 0 0.25em;
+ margin: 0 1em 0 0;
+ border: none;
+ line-height: unset;
+ box-sizing: border-box;
+ }
+}
+.Differences {
+ width: 100%;
+ table-layout: fixed;
+ empty-cells: show;
+}
+
+.Differences thead {
+ display: none;
+}
+
+.Differences thead th {
+ text-align: left;
+ padding-left: 4 / 14 * 16em;
+}
+.Differences tbody th {
+ text-align: right;
+ width: 4em;
+ padding: 1px 2px;
+ border-right: 1px solid @gray-light;
+ background: @gray-lightest;
+ font-weight: normal;
+ vertical-align: top;
+}
+
+.Differences tbody td {
+ width: 50%;
+ .preformatted();
+ word-break: break-all;
+}
+
+@color-diff-ins: #bfb;
+@color-diff-del: #faa;
+@color-diff-changed-old: #fdd;
+@color-diff-changed-new: #efe;
+
+.DifferencesSideBySide {
+ ins, del {
+ text-decoration: none;
+ }
+
+ .ChangeInsert {
+ td.Left {
+ background: @gray-lighter;
+ }
+ td.Right {
+ background: @color-diff-ins;
+ }
+ }
+
+ .ChangeDelete {
+ td.Left {
+ background: @color-diff-del;
+ }
+ td.Right {
+ background: @gray-lighter;
+ }
+ }
+
+ .ChangeReplace {
+ td.Left {
+ background: @color-diff-changed-old;
+ del {
+ background: @color-diff-del;
+ }
+ }
+
+ td.Right {
+ background: @color-diff-changed-new;
+ ins {
+ background: @color-diff-ins;
+ }
+ }
+
+ }
+}
+
+.Differences .Skipped {
+ background: @gray-lightest;
+}
+
+.DifferencesInline .ChangeReplace .Left,
+.DifferencesInline .ChangeDelete .Left {
+ background: #fdd;
+}
+
+.DifferencesInline .ChangeReplace .Right,
+.DifferencesInline .ChangeInsert .Right {
+ background: #dfd;
+}
+
+.DifferencesInline .ChangeReplace ins {
+ background: #9e9;
+}
+
+.DifferencesInline .ChangeReplace del {
+ background: #e99;
+}
diff --git a/public/js/combined.js b/public/js/combined.js
new file mode 100644
index 0000000..83b19a5
--- /dev/null
+++ b/public/js/combined.js
@@ -0,0 +1,69 @@
+(function(window, $) {
+ 'use strict';
+
+ var Web = function () {
+ };
+
+ Web.prototype = {
+ initialize: function (icinga) {
+ this.icinga = icinga;
+ $(document).on('focus', 'form.gipfl-form input, form.gipfl-form textarea, form.gipfl-form select', this.formElementFocus);
+ $(document).on('click', '.gipfl-collapsible-control', this.toggleCollapsible);
+ },
+
+ toggleCollapsible: function (ev) {
+ var $toggle = $(ev.currentTarget);
+ var $collapsible = $toggle.parent();
+ $collapsible.toggleClass('collapsed');
+ },
+
+ formElementFocus: function (ev) {
+ var $input = $(ev.currentTarget);
+ if ($input.closest('form.editor').length) {
+ return;
+ }
+ var $set = $input.closest('.extensible-set');
+ if ($set.length) {
+ var $textInputs = $('input[type=text]', $set);
+ if ($textInputs.length > 1) {
+ $textInputs.not(':first').attr('tabIndex', '-1');
+ }
+ }
+
+ var $dd = $input.closest('dd');
+ if ($dd.attr('id') && $dd.attr('id').match(/button/)) {
+ return;
+ }
+ var $li = $input.closest('li');
+ var $dt = $dd.prev();
+ var $form = $dd.closest('form');
+
+ $form.find('dt, dd, dl, li').removeClass('active');
+ $li.addClass('active');
+ $dt.addClass('active');
+ $dd.addClass('active');
+ $dt.closest('dl').addClass('active');
+ },
+
+ highlightFormErrors: function ($container) {
+ $container.find('dd ul.errors').each(function (idx, ul) {
+ var $ul = $(ul);
+ var $dd = $ul.closest('dd');
+ var $dt = $dd.prev();
+
+ $dt.addClass('errors');
+ $dd.addClass('errors');
+ });
+ },
+
+ toggleFieldset: function (ev) {
+ ev.stopPropagation();
+ var $fieldset = $(ev.currentTarget).closest('fieldset');
+ $fieldset.toggleClass('collapsed');
+ this.fixFieldsetInfo($fieldset);
+ this.openedFieldsets[$fieldset.attr('id')] = ! $fieldset.hasClass('collapsed');
+ }
+ };
+
+ window.incubatorComponentLoader.addComponent(new Web());
+})(window, jQuery);
diff --git a/public/js/loader.js b/public/js/loader.js
new file mode 100644
index 0000000..6cd3073
--- /dev/null
+++ b/public/js/loader.js
@@ -0,0 +1,54 @@
+(function(window, $) {
+ 'use strict';
+
+ var IncubatorComponentLoader = function () {
+ this.components = [];
+ };
+
+ IncubatorComponentLoader.prototype = {
+ initialize: function (icinga) {
+ this.icinga = icinga;
+ // Trigger module loading - always
+ icinga.module('incubator');
+ $.each(this.components, function (_, component) {
+ component.initialize(icinga);
+ });
+ icinga.logger.info('Incubator is ready');
+ },
+
+ addComponent: function (component) {
+ this.components.push(component);
+ },
+
+ destroy: function () {
+ // Eventually: this.unbindEventHandlers();
+
+ $.each(this.components, function (_, component) {
+ if (typeof component.destroy === 'function') {
+ component.destroy();
+ }
+ });
+
+ this.components = [];
+ this.icinga = null;
+ }
+ };
+
+ var startup;
+ var w = window;
+ function safeLaunch()
+ {
+ if (typeof(w.icinga) !== 'undefined' && w.icinga.initialized) {
+ clearInterval(startup);
+ w.incubatorComponentLoader.initialize(w.icinga);
+ } else {
+ console.log('Incubator module is still waiting for icinga');
+ }
+ }
+
+ $(document).ready(function () {
+ startup = setInterval(safeLaunch, 30);
+ safeLaunch();
+ });
+ w.incubatorComponentLoader = new IncubatorComponentLoader();
+})(window, jQuery);
diff --git a/public/js/module.js b/public/js/module.js
new file mode 100644
index 0000000..ff4086f
--- /dev/null
+++ b/public/js/module.js
@@ -0,0 +1,19 @@
+(function(window, $) {
+ 'use strict';
+
+ var Incubator = function (icinga) {
+ this.icinga = icinga;
+ };
+
+ Incubator.prototype = {
+ initialize: function (icinga) {
+ },
+
+ destroy: function () {
+ window.incubatorComponentLoader.destroy();
+ this.icinga = null;
+ }
+ };
+
+ Icinga.availableModules.incubator = Incubator;
+})(window, jQuery);
diff --git a/run.php b/run.php
new file mode 100644
index 0000000..319f6e3
--- /dev/null
+++ b/run.php
@@ -0,0 +1,14 @@
+<?php
+
+use Icinga\Module\Incubator\Styles;
+
+// We extend Less files shipped per default
+if (isset($_SERVER['REQUEST_URI'])
+ && preg_match('#/css/icinga(?:\.min)?\.css#', $_SERVER['REQUEST_URI'])
+ && ! class_exists(__NAMESPACE__ . '\\Styles')) {
+ require __DIR__ . '/extendStyles.php';
+ $styles = new Styles();
+ return;
+}
+
+require_once __DIR__ . '/vendor/autoload.php';
diff --git a/vendor/autoload.php b/vendor/autoload.php
new file mode 100644
index 0000000..4be7d28
--- /dev/null
+++ b/vendor/autoload.php
@@ -0,0 +1,7 @@
+<?php
+
+// autoload.php @generated by Composer
+
+require_once __DIR__ . '/composer/autoload_real.php';
+
+return ComposerAutoloaderInit684800034bdac0af606e9381dda72632::getLoader();
diff --git a/vendor/composer/ClassLoader.php b/vendor/composer/ClassLoader.php
new file mode 100644
index 0000000..0cd6055
--- /dev/null
+++ b/vendor/composer/ClassLoader.php
@@ -0,0 +1,572 @@
+<?php
+
+/*
+ * This file is part of Composer.
+ *
+ * (c) Nils Adermann <naderman@naderman.de>
+ * Jordi Boggiano <j.boggiano@seld.be>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Composer\Autoload;
+
+/**
+ * ClassLoader implements a PSR-0, PSR-4 and classmap class loader.
+ *
+ * $loader = new \Composer\Autoload\ClassLoader();
+ *
+ * // register classes with namespaces
+ * $loader->add('Symfony\Component', __DIR__.'/component');
+ * $loader->add('Symfony', __DIR__.'/framework');
+ *
+ * // activate the autoloader
+ * $loader->register();
+ *
+ * // to enable searching the include path (eg. for PEAR packages)
+ * $loader->setUseIncludePath(true);
+ *
+ * In this example, if you try to use a class in the Symfony\Component
+ * namespace or one of its children (Symfony\Component\Console for instance),
+ * the autoloader will first look for the class under the component/
+ * directory, and it will then fallback to the framework/ directory if not
+ * found before giving up.
+ *
+ * This class is loosely based on the Symfony UniversalClassLoader.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ * @author Jordi Boggiano <j.boggiano@seld.be>
+ * @see https://www.php-fig.org/psr/psr-0/
+ * @see https://www.php-fig.org/psr/psr-4/
+ */
+class ClassLoader
+{
+ /** @var ?string */
+ private $vendorDir;
+
+ // PSR-4
+ /**
+ * @var array[]
+ * @psalm-var array<string, array<string, int>>
+ */
+ private $prefixLengthsPsr4 = array();
+ /**
+ * @var array[]
+ * @psalm-var array<string, array<int, string>>
+ */
+ private $prefixDirsPsr4 = array();
+ /**
+ * @var array[]
+ * @psalm-var array<string, string>
+ */
+ private $fallbackDirsPsr4 = array();
+
+ // PSR-0
+ /**
+ * @var array[]
+ * @psalm-var array<string, array<string, string[]>>
+ */
+ private $prefixesPsr0 = array();
+ /**
+ * @var array[]
+ * @psalm-var array<string, string>
+ */
+ private $fallbackDirsPsr0 = array();
+
+ /** @var bool */
+ private $useIncludePath = false;
+
+ /**
+ * @var string[]
+ * @psalm-var array<string, string>
+ */
+ private $classMap = array();
+
+ /** @var bool */
+ private $classMapAuthoritative = false;
+
+ /**
+ * @var bool[]
+ * @psalm-var array<string, bool>
+ */
+ private $missingClasses = array();
+
+ /** @var ?string */
+ private $apcuPrefix;
+
+ /**
+ * @var self[]
+ */
+ private static $registeredLoaders = array();
+
+ /**
+ * @param ?string $vendorDir
+ */
+ public function __construct($vendorDir = null)
+ {
+ $this->vendorDir = $vendorDir;
+ }
+
+ /**
+ * @return string[]
+ */
+ public function getPrefixes()
+ {
+ if (!empty($this->prefixesPsr0)) {
+ return call_user_func_array('array_merge', array_values($this->prefixesPsr0));
+ }
+
+ return array();
+ }
+
+ /**
+ * @return array[]
+ * @psalm-return array<string, array<int, string>>
+ */
+ public function getPrefixesPsr4()
+ {
+ return $this->prefixDirsPsr4;
+ }
+
+ /**
+ * @return array[]
+ * @psalm-return array<string, string>
+ */
+ public function getFallbackDirs()
+ {
+ return $this->fallbackDirsPsr0;
+ }
+
+ /**
+ * @return array[]
+ * @psalm-return array<string, string>
+ */
+ public function getFallbackDirsPsr4()
+ {
+ return $this->fallbackDirsPsr4;
+ }
+
+ /**
+ * @return string[] Array of classname => path
+ * @psalm-var array<string, string>
+ */
+ public function getClassMap()
+ {
+ return $this->classMap;
+ }
+
+ /**
+ * @param string[] $classMap Class to filename map
+ * @psalm-param array<string, string> $classMap
+ *
+ * @return void
+ */
+ public function addClassMap(array $classMap)
+ {
+ if ($this->classMap) {
+ $this->classMap = array_merge($this->classMap, $classMap);
+ } else {
+ $this->classMap = $classMap;
+ }
+ }
+
+ /**
+ * Registers a set of PSR-0 directories for a given prefix, either
+ * appending or prepending to the ones previously set for this prefix.
+ *
+ * @param string $prefix The prefix
+ * @param string[]|string $paths The PSR-0 root directories
+ * @param bool $prepend Whether to prepend the directories
+ *
+ * @return void
+ */
+ public function add($prefix, $paths, $prepend = false)
+ {
+ if (!$prefix) {
+ if ($prepend) {
+ $this->fallbackDirsPsr0 = array_merge(
+ (array) $paths,
+ $this->fallbackDirsPsr0
+ );
+ } else {
+ $this->fallbackDirsPsr0 = array_merge(
+ $this->fallbackDirsPsr0,
+ (array) $paths
+ );
+ }
+
+ return;
+ }
+
+ $first = $prefix[0];
+ if (!isset($this->prefixesPsr0[$first][$prefix])) {
+ $this->prefixesPsr0[$first][$prefix] = (array) $paths;
+
+ return;
+ }
+ if ($prepend) {
+ $this->prefixesPsr0[$first][$prefix] = array_merge(
+ (array) $paths,
+ $this->prefixesPsr0[$first][$prefix]
+ );
+ } else {
+ $this->prefixesPsr0[$first][$prefix] = array_merge(
+ $this->prefixesPsr0[$first][$prefix],
+ (array) $paths
+ );
+ }
+ }
+
+ /**
+ * Registers a set of PSR-4 directories for a given namespace, either
+ * appending or prepending to the ones previously set for this namespace.
+ *
+ * @param string $prefix The prefix/namespace, with trailing '\\'
+ * @param string[]|string $paths The PSR-4 base directories
+ * @param bool $prepend Whether to prepend the directories
+ *
+ * @throws \InvalidArgumentException
+ *
+ * @return void
+ */
+ public function addPsr4($prefix, $paths, $prepend = false)
+ {
+ if (!$prefix) {
+ // Register directories for the root namespace.
+ if ($prepend) {
+ $this->fallbackDirsPsr4 = array_merge(
+ (array) $paths,
+ $this->fallbackDirsPsr4
+ );
+ } else {
+ $this->fallbackDirsPsr4 = array_merge(
+ $this->fallbackDirsPsr4,
+ (array) $paths
+ );
+ }
+ } elseif (!isset($this->prefixDirsPsr4[$prefix])) {
+ // Register directories for a new namespace.
+ $length = strlen($prefix);
+ if ('\\' !== $prefix[$length - 1]) {
+ throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
+ }
+ $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
+ $this->prefixDirsPsr4[$prefix] = (array) $paths;
+ } elseif ($prepend) {
+ // Prepend directories for an already registered namespace.
+ $this->prefixDirsPsr4[$prefix] = array_merge(
+ (array) $paths,
+ $this->prefixDirsPsr4[$prefix]
+ );
+ } else {
+ // Append directories for an already registered namespace.
+ $this->prefixDirsPsr4[$prefix] = array_merge(
+ $this->prefixDirsPsr4[$prefix],
+ (array) $paths
+ );
+ }
+ }
+
+ /**
+ * Registers a set of PSR-0 directories for a given prefix,
+ * replacing any others previously set for this prefix.
+ *
+ * @param string $prefix The prefix
+ * @param string[]|string $paths The PSR-0 base directories
+ *
+ * @return void
+ */
+ public function set($prefix, $paths)
+ {
+ if (!$prefix) {
+ $this->fallbackDirsPsr0 = (array) $paths;
+ } else {
+ $this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
+ }
+ }
+
+ /**
+ * Registers a set of PSR-4 directories for a given namespace,
+ * replacing any others previously set for this namespace.
+ *
+ * @param string $prefix The prefix/namespace, with trailing '\\'
+ * @param string[]|string $paths The PSR-4 base directories
+ *
+ * @throws \InvalidArgumentException
+ *
+ * @return void
+ */
+ public function setPsr4($prefix, $paths)
+ {
+ if (!$prefix) {
+ $this->fallbackDirsPsr4 = (array) $paths;
+ } else {
+ $length = strlen($prefix);
+ if ('\\' !== $prefix[$length - 1]) {
+ throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
+ }
+ $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
+ $this->prefixDirsPsr4[$prefix] = (array) $paths;
+ }
+ }
+
+ /**
+ * Turns on searching the include path for class files.
+ *
+ * @param bool $useIncludePath
+ *
+ * @return void
+ */
+ public function setUseIncludePath($useIncludePath)
+ {
+ $this->useIncludePath = $useIncludePath;
+ }
+
+ /**
+ * Can be used to check if the autoloader uses the include path to check
+ * for classes.
+ *
+ * @return bool
+ */
+ public function getUseIncludePath()
+ {
+ return $this->useIncludePath;
+ }
+
+ /**
+ * Turns off searching the prefix and fallback directories for classes
+ * that have not been registered with the class map.
+ *
+ * @param bool $classMapAuthoritative
+ *
+ * @return void
+ */
+ public function setClassMapAuthoritative($classMapAuthoritative)
+ {
+ $this->classMapAuthoritative = $classMapAuthoritative;
+ }
+
+ /**
+ * Should class lookup fail if not found in the current class map?
+ *
+ * @return bool
+ */
+ public function isClassMapAuthoritative()
+ {
+ return $this->classMapAuthoritative;
+ }
+
+ /**
+ * APCu prefix to use to cache found/not-found classes, if the extension is enabled.
+ *
+ * @param string|null $apcuPrefix
+ *
+ * @return void
+ */
+ public function setApcuPrefix($apcuPrefix)
+ {
+ $this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null;
+ }
+
+ /**
+ * The APCu prefix in use, or null if APCu caching is not enabled.
+ *
+ * @return string|null
+ */
+ public function getApcuPrefix()
+ {
+ return $this->apcuPrefix;
+ }
+
+ /**
+ * Registers this instance as an autoloader.
+ *
+ * @param bool $prepend Whether to prepend the autoloader or not
+ *
+ * @return void
+ */
+ public function register($prepend = false)
+ {
+ spl_autoload_register(array($this, 'loadClass'), true, $prepend);
+
+ if (null === $this->vendorDir) {
+ return;
+ }
+
+ if ($prepend) {
+ self::$registeredLoaders = array($this->vendorDir => $this) + self::$registeredLoaders;
+ } else {
+ unset(self::$registeredLoaders[$this->vendorDir]);
+ self::$registeredLoaders[$this->vendorDir] = $this;
+ }
+ }
+
+ /**
+ * Unregisters this instance as an autoloader.
+ *
+ * @return void
+ */
+ public function unregister()
+ {
+ spl_autoload_unregister(array($this, 'loadClass'));
+
+ if (null !== $this->vendorDir) {
+ unset(self::$registeredLoaders[$this->vendorDir]);
+ }
+ }
+
+ /**
+ * Loads the given class or interface.
+ *
+ * @param string $class The name of the class
+ * @return true|null True if loaded, null otherwise
+ */
+ public function loadClass($class)
+ {
+ if ($file = $this->findFile($class)) {
+ includeFile($file);
+
+ return true;
+ }
+
+ return null;
+ }
+
+ /**
+ * Finds the path to the file where the class is defined.
+ *
+ * @param string $class The name of the class
+ *
+ * @return string|false The path if found, false otherwise
+ */
+ public function findFile($class)
+ {
+ // class map lookup
+ if (isset($this->classMap[$class])) {
+ return $this->classMap[$class];
+ }
+ if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) {
+ return false;
+ }
+ if (null !== $this->apcuPrefix) {
+ $file = apcu_fetch($this->apcuPrefix.$class, $hit);
+ if ($hit) {
+ return $file;
+ }
+ }
+
+ $file = $this->findFileWithExtension($class, '.php');
+
+ // Search for Hack files if we are running on HHVM
+ if (false === $file && defined('HHVM_VERSION')) {
+ $file = $this->findFileWithExtension($class, '.hh');
+ }
+
+ if (null !== $this->apcuPrefix) {
+ apcu_add($this->apcuPrefix.$class, $file);
+ }
+
+ if (false === $file) {
+ // Remember that this class does not exist.
+ $this->missingClasses[$class] = true;
+ }
+
+ return $file;
+ }
+
+ /**
+ * Returns the currently registered loaders indexed by their corresponding vendor directories.
+ *
+ * @return self[]
+ */
+ public static function getRegisteredLoaders()
+ {
+ return self::$registeredLoaders;
+ }
+
+ /**
+ * @param string $class
+ * @param string $ext
+ * @return string|false
+ */
+ private function findFileWithExtension($class, $ext)
+ {
+ // PSR-4 lookup
+ $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
+
+ $first = $class[0];
+ if (isset($this->prefixLengthsPsr4[$first])) {
+ $subPath = $class;
+ while (false !== $lastPos = strrpos($subPath, '\\')) {
+ $subPath = substr($subPath, 0, $lastPos);
+ $search = $subPath . '\\';
+ if (isset($this->prefixDirsPsr4[$search])) {
+ $pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1);
+ foreach ($this->prefixDirsPsr4[$search] as $dir) {
+ if (file_exists($file = $dir . $pathEnd)) {
+ return $file;
+ }
+ }
+ }
+ }
+ }
+
+ // PSR-4 fallback dirs
+ foreach ($this->fallbackDirsPsr4 as $dir) {
+ if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
+ return $file;
+ }
+ }
+
+ // PSR-0 lookup
+ if (false !== $pos = strrpos($class, '\\')) {
+ // namespaced class name
+ $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
+ . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
+ } else {
+ // PEAR-like class name
+ $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
+ }
+
+ if (isset($this->prefixesPsr0[$first])) {
+ foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
+ if (0 === strpos($class, $prefix)) {
+ foreach ($dirs as $dir) {
+ if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
+ return $file;
+ }
+ }
+ }
+ }
+ }
+
+ // PSR-0 fallback dirs
+ foreach ($this->fallbackDirsPsr0 as $dir) {
+ if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
+ return $file;
+ }
+ }
+
+ // PSR-0 include paths.
+ if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
+ return $file;
+ }
+
+ return false;
+ }
+}
+
+/**
+ * Scope isolated include.
+ *
+ * Prevents access to $this/self from included files.
+ *
+ * @param string $file
+ * @return void
+ * @private
+ */
+function includeFile($file)
+{
+ include $file;
+}
diff --git a/vendor/composer/InstalledVersions.php b/vendor/composer/InstalledVersions.php
new file mode 100644
index 0000000..7c5502c
--- /dev/null
+++ b/vendor/composer/InstalledVersions.php
@@ -0,0 +1,337 @@
+<?php
+
+/*
+ * This file is part of Composer.
+ *
+ * (c) Nils Adermann <naderman@naderman.de>
+ * Jordi Boggiano <j.boggiano@seld.be>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Composer;
+
+use Composer\Autoload\ClassLoader;
+use Composer\Semver\VersionParser;
+
+/**
+ * This class is copied in every Composer installed project and available to all
+ *
+ * See also https://getcomposer.org/doc/07-runtime.md#installed-versions
+ *
+ * To require its presence, you can require `composer-runtime-api ^2.0`
+ */
+class InstalledVersions
+{
+ private static $installed;
+ private static $canGetVendors;
+ private static $installedByVendor = array();
+
+ /**
+ * Returns a list of all package names which are present, either by being installed, replaced or provided
+ *
+ * @return string[]
+ * @psalm-return list<string>
+ */
+ public static function getInstalledPackages()
+ {
+ $packages = array();
+ foreach (self::getInstalled() as $installed) {
+ $packages[] = array_keys($installed['versions']);
+ }
+
+ if (1 === \count($packages)) {
+ return $packages[0];
+ }
+
+ return array_keys(array_flip(\call_user_func_array('array_merge', $packages)));
+ }
+
+ /**
+ * Returns a list of all package names with a specific type e.g. 'library'
+ *
+ * @param string $type
+ * @return string[]
+ * @psalm-return list<string>
+ */
+ public static function getInstalledPackagesByType($type)
+ {
+ $packagesByType = array();
+
+ foreach (self::getInstalled() as $installed) {
+ foreach ($installed['versions'] as $name => $package) {
+ if (isset($package['type']) && $package['type'] === $type) {
+ $packagesByType[] = $name;
+ }
+ }
+ }
+
+ return $packagesByType;
+ }
+
+ /**
+ * Checks whether the given package is installed
+ *
+ * This also returns true if the package name is provided or replaced by another package
+ *
+ * @param string $packageName
+ * @param bool $includeDevRequirements
+ * @return bool
+ */
+ public static function isInstalled($packageName, $includeDevRequirements = true)
+ {
+ foreach (self::getInstalled() as $installed) {
+ if (isset($installed['versions'][$packageName])) {
+ return $includeDevRequirements || empty($installed['versions'][$packageName]['dev_requirement']);
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Checks whether the given package satisfies a version constraint
+ *
+ * e.g. If you want to know whether version 2.3+ of package foo/bar is installed, you would call:
+ *
+ * Composer\InstalledVersions::satisfies(new VersionParser, 'foo/bar', '^2.3')
+ *
+ * @param VersionParser $parser Install composer/semver to have access to this class and functionality
+ * @param string $packageName
+ * @param string|null $constraint A version constraint to check for, if you pass one you have to make sure composer/semver is required by your package
+ * @return bool
+ */
+ public static function satisfies(VersionParser $parser, $packageName, $constraint)
+ {
+ $constraint = $parser->parseConstraints($constraint);
+ $provided = $parser->parseConstraints(self::getVersionRanges($packageName));
+
+ return $provided->matches($constraint);
+ }
+
+ /**
+ * Returns a version constraint representing all the range(s) which are installed for a given package
+ *
+ * It is easier to use this via isInstalled() with the $constraint argument if you need to check
+ * whether a given version of a package is installed, and not just whether it exists
+ *
+ * @param string $packageName
+ * @return string Version constraint usable with composer/semver
+ */
+ public static function getVersionRanges($packageName)
+ {
+ foreach (self::getInstalled() as $installed) {
+ if (!isset($installed['versions'][$packageName])) {
+ continue;
+ }
+
+ $ranges = array();
+ if (isset($installed['versions'][$packageName]['pretty_version'])) {
+ $ranges[] = $installed['versions'][$packageName]['pretty_version'];
+ }
+ if (array_key_exists('aliases', $installed['versions'][$packageName])) {
+ $ranges = array_merge($ranges, $installed['versions'][$packageName]['aliases']);
+ }
+ if (array_key_exists('replaced', $installed['versions'][$packageName])) {
+ $ranges = array_merge($ranges, $installed['versions'][$packageName]['replaced']);
+ }
+ if (array_key_exists('provided', $installed['versions'][$packageName])) {
+ $ranges = array_merge($ranges, $installed['versions'][$packageName]['provided']);
+ }
+
+ return implode(' || ', $ranges);
+ }
+
+ throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
+ }
+
+ /**
+ * @param string $packageName
+ * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present
+ */
+ public static function getVersion($packageName)
+ {
+ foreach (self::getInstalled() as $installed) {
+ if (!isset($installed['versions'][$packageName])) {
+ continue;
+ }
+
+ if (!isset($installed['versions'][$packageName]['version'])) {
+ return null;
+ }
+
+ return $installed['versions'][$packageName]['version'];
+ }
+
+ throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
+ }
+
+ /**
+ * @param string $packageName
+ * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present
+ */
+ public static function getPrettyVersion($packageName)
+ {
+ foreach (self::getInstalled() as $installed) {
+ if (!isset($installed['versions'][$packageName])) {
+ continue;
+ }
+
+ if (!isset($installed['versions'][$packageName]['pretty_version'])) {
+ return null;
+ }
+
+ return $installed['versions'][$packageName]['pretty_version'];
+ }
+
+ throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
+ }
+
+ /**
+ * @param string $packageName
+ * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as reference
+ */
+ public static function getReference($packageName)
+ {
+ foreach (self::getInstalled() as $installed) {
+ if (!isset($installed['versions'][$packageName])) {
+ continue;
+ }
+
+ if (!isset($installed['versions'][$packageName]['reference'])) {
+ return null;
+ }
+
+ return $installed['versions'][$packageName]['reference'];
+ }
+
+ throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
+ }
+
+ /**
+ * @param string $packageName
+ * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as install path. Packages of type metapackages also have a null install path.
+ */
+ public static function getInstallPath($packageName)
+ {
+ foreach (self::getInstalled() as $installed) {
+ if (!isset($installed['versions'][$packageName])) {
+ continue;
+ }
+
+ return isset($installed['versions'][$packageName]['install_path']) ? $installed['versions'][$packageName]['install_path'] : null;
+ }
+
+ throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
+ }
+
+ /**
+ * @return array
+ * @psalm-return array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}
+ */
+ public static function getRootPackage()
+ {
+ $installed = self::getInstalled();
+
+ return $installed[0]['root'];
+ }
+
+ /**
+ * Returns the raw installed.php data for custom implementations
+ *
+ * @deprecated Use getAllRawData() instead which returns all datasets for all autoloaders present in the process. getRawData only returns the first dataset loaded, which may not be what you expect.
+ * @return array[]
+ * @psalm-return array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>}
+ */
+ public static function getRawData()
+ {
+ @trigger_error('getRawData only returns the first dataset loaded, which may not be what you expect. Use getAllRawData() instead which returns all datasets for all autoloaders present in the process.', E_USER_DEPRECATED);
+
+ if (null === self::$installed) {
+ // only require the installed.php file if this file is loaded from its dumped location,
+ // and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
+ if (substr(__DIR__, -8, 1) !== 'C') {
+ self::$installed = include __DIR__ . '/installed.php';
+ } else {
+ self::$installed = array();
+ }
+ }
+
+ return self::$installed;
+ }
+
+ /**
+ * Returns the raw data of all installed.php which are currently loaded for custom implementations
+ *
+ * @return array[]
+ * @psalm-return list<array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>}>
+ */
+ public static function getAllRawData()
+ {
+ return self::getInstalled();
+ }
+
+ /**
+ * Lets you reload the static array from another file
+ *
+ * This is only useful for complex integrations in which a project needs to use
+ * this class but then also needs to execute another project's autoloader in process,
+ * and wants to ensure both projects have access to their version of installed.php.
+ *
+ * A typical case would be PHPUnit, where it would need to make sure it reads all
+ * the data it needs from this class, then call reload() with
+ * `require $CWD/vendor/composer/installed.php` (or similar) as input to make sure
+ * the project in which it runs can then also use this class safely, without
+ * interference between PHPUnit's dependencies and the project's dependencies.
+ *
+ * @param array[] $data A vendor/composer/installed.php data set
+ * @return void
+ *
+ * @psalm-param array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>} $data
+ */
+ public static function reload($data)
+ {
+ self::$installed = $data;
+ self::$installedByVendor = array();
+ }
+
+ /**
+ * @return array[]
+ * @psalm-return list<array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>}>
+ */
+ private static function getInstalled()
+ {
+ if (null === self::$canGetVendors) {
+ self::$canGetVendors = method_exists('Composer\Autoload\ClassLoader', 'getRegisteredLoaders');
+ }
+
+ $installed = array();
+
+ if (self::$canGetVendors) {
+ foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) {
+ if (isset(self::$installedByVendor[$vendorDir])) {
+ $installed[] = self::$installedByVendor[$vendorDir];
+ } elseif (is_file($vendorDir.'/composer/installed.php')) {
+ $installed[] = self::$installedByVendor[$vendorDir] = require $vendorDir.'/composer/installed.php';
+ if (null === self::$installed && strtr($vendorDir.'/composer', '\\', '/') === strtr(__DIR__, '\\', '/')) {
+ self::$installed = $installed[count($installed) - 1];
+ }
+ }
+ }
+ }
+
+ if (null === self::$installed) {
+ // only require the installed.php file if this file is loaded from its dumped location,
+ // and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
+ if (substr(__DIR__, -8, 1) !== 'C') {
+ self::$installed = require __DIR__ . '/installed.php';
+ } else {
+ self::$installed = array();
+ }
+ }
+ $installed[] = self::$installed;
+
+ return $installed;
+ }
+}
diff --git a/vendor/composer/LICENSE b/vendor/composer/LICENSE
new file mode 100644
index 0000000..f27399a
--- /dev/null
+++ b/vendor/composer/LICENSE
@@ -0,0 +1,21 @@
+
+Copyright (c) Nils Adermann, Jordi Boggiano
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is furnished
+to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
diff --git a/vendor/composer/autoload_classmap.php b/vendor/composer/autoload_classmap.php
new file mode 100644
index 0000000..b26f1b1
--- /dev/null
+++ b/vendor/composer/autoload_classmap.php
@@ -0,0 +1,10 @@
+<?php
+
+// autoload_classmap.php @generated by Composer
+
+$vendorDir = dirname(dirname(__FILE__));
+$baseDir = dirname($vendorDir);
+
+return array(
+ 'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php',
+);
diff --git a/vendor/composer/autoload_namespaces.php b/vendor/composer/autoload_namespaces.php
new file mode 100644
index 0000000..b7fc012
--- /dev/null
+++ b/vendor/composer/autoload_namespaces.php
@@ -0,0 +1,9 @@
+<?php
+
+// autoload_namespaces.php @generated by Composer
+
+$vendorDir = dirname(dirname(__FILE__));
+$baseDir = dirname($vendorDir);
+
+return array(
+);
diff --git a/vendor/composer/autoload_psr4.php b/vendor/composer/autoload_psr4.php
new file mode 100644
index 0000000..b3948b3
--- /dev/null
+++ b/vendor/composer/autoload_psr4.php
@@ -0,0 +1,38 @@
+<?php
+
+// autoload_psr4.php @generated by Composer
+
+$vendorDir = dirname(dirname(__FILE__));
+$baseDir = dirname($vendorDir);
+
+return array(
+ 'gipfl\\ZfDb\\' => array($vendorDir . '/gipfl/zfdb/src'),
+ 'gipfl\\ZfDbStore\\' => array($vendorDir . '/gipfl/zfdbstore/src'),
+ 'gipfl\\Web\\' => array($vendorDir . '/gipfl/web/src'),
+ 'gipfl\\Translation\\' => array($vendorDir . '/gipfl/translation/src'),
+ 'gipfl\\SystemD\\' => array($vendorDir . '/gipfl/systemd/src'),
+ 'gipfl\\Stream\\' => array($vendorDir . '/gipfl/stream/src'),
+ 'gipfl\\Socket\\' => array($vendorDir . '/gipfl/socket/src'),
+ 'gipfl\\SimpleDaemon\\' => array($vendorDir . '/gipfl/simple-daemon/src'),
+ 'gipfl\\ReactUtils\\' => array($vendorDir . '/gipfl/react-utils/src'),
+ 'gipfl\\Protocol\\NetString\\' => array($vendorDir . '/gipfl/protocol-netstring/src'),
+ 'gipfl\\Protocol\\JsonRpc\\' => array($vendorDir . '/gipfl/protocol-jsonrpc/src'),
+ 'gipfl\\Protocol\\Generic\\' => array($vendorDir . '/gipfl/protocol/src/Generic'),
+ 'gipfl\\Protocol\\Exception\\' => array($vendorDir . '/gipfl/protocol/src/Exception'),
+ 'gipfl\\Process\\' => array($vendorDir . '/gipfl/process/src'),
+ 'gipfl\\OpenRpc\\' => array($vendorDir . '/gipfl/openrpc/src'),
+ 'gipfl\\Log\\' => array($vendorDir . '/gipfl/log/src'),
+ 'gipfl\\LinuxHealth\\' => array($vendorDir . '/gipfl/linux-health/src'),
+ 'gipfl\\Json\\' => array($vendorDir . '/gipfl/json/src'),
+ 'gipfl\\InfluxDb\\' => array($vendorDir . '/gipfl/influxdb/src'),
+ 'gipfl\\IcingaWeb2\\' => array($vendorDir . '/gipfl/icingaweb2/src'),
+ 'gipfl\\IcingaCliDaemon\\' => array($vendorDir . '/gipfl/icinga-cli-daemon/src'),
+ 'gipfl\\Format\\' => array($vendorDir . '/gipfl/format/src'),
+ 'gipfl\\Diff\\' => array($vendorDir . '/gipfl/diff/src'),
+ 'gipfl\\DbMigration\\' => array($vendorDir . '/gipfl/db-migration/src'),
+ 'gipfl\\DataType\\' => array($vendorDir . '/gipfl/data-type/src'),
+ 'gipfl\\Curl\\' => array($vendorDir . '/gipfl/curl/src'),
+ 'gipfl\\Cli\\' => array($vendorDir . '/gipfl/cli/src'),
+ 'gipfl\\Calendar\\' => array($vendorDir . '/gipfl/calendar/src'),
+ 'Psr\\Log\\' => array($vendorDir . '/psr/log/Psr/Log'),
+);
diff --git a/vendor/composer/autoload_real.php b/vendor/composer/autoload_real.php
new file mode 100644
index 0000000..2fe188a
--- /dev/null
+++ b/vendor/composer/autoload_real.php
@@ -0,0 +1,57 @@
+<?php
+
+// autoload_real.php @generated by Composer
+
+class ComposerAutoloaderInit684800034bdac0af606e9381dda72632
+{
+ private static $loader;
+
+ public static function loadClassLoader($class)
+ {
+ if ('Composer\Autoload\ClassLoader' === $class) {
+ require __DIR__ . '/ClassLoader.php';
+ }
+ }
+
+ /**
+ * @return \Composer\Autoload\ClassLoader
+ */
+ public static function getLoader()
+ {
+ if (null !== self::$loader) {
+ return self::$loader;
+ }
+
+ require __DIR__ . '/platform_check.php';
+
+ spl_autoload_register(array('ComposerAutoloaderInit684800034bdac0af606e9381dda72632', 'loadClassLoader'), true, true);
+ self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(\dirname(__FILE__)));
+ spl_autoload_unregister(array('ComposerAutoloaderInit684800034bdac0af606e9381dda72632', 'loadClassLoader'));
+
+ $useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
+ if ($useStaticLoader) {
+ require __DIR__ . '/autoload_static.php';
+
+ call_user_func(\Composer\Autoload\ComposerStaticInit684800034bdac0af606e9381dda72632::getInitializer($loader));
+ } else {
+ $map = require __DIR__ . '/autoload_namespaces.php';
+ foreach ($map as $namespace => $path) {
+ $loader->set($namespace, $path);
+ }
+
+ $map = require __DIR__ . '/autoload_psr4.php';
+ foreach ($map as $namespace => $path) {
+ $loader->setPsr4($namespace, $path);
+ }
+
+ $classMap = require __DIR__ . '/autoload_classmap.php';
+ if ($classMap) {
+ $loader->addClassMap($classMap);
+ }
+ }
+
+ $loader->register(true);
+
+ return $loader;
+ }
+}
diff --git a/vendor/composer/autoload_static.php b/vendor/composer/autoload_static.php
new file mode 100644
index 0000000..688c590
--- /dev/null
+++ b/vendor/composer/autoload_static.php
@@ -0,0 +1,179 @@
+<?php
+
+// autoload_static.php @generated by Composer
+
+namespace Composer\Autoload;
+
+class ComposerStaticInit684800034bdac0af606e9381dda72632
+{
+ public static $prefixLengthsPsr4 = array (
+ 'g' =>
+ array (
+ 'gipfl\\ZfDb\\' => 11,
+ 'gipfl\\ZfDbStore\\' => 16,
+ 'gipfl\\Web\\' => 10,
+ 'gipfl\\Translation\\' => 18,
+ 'gipfl\\SystemD\\' => 14,
+ 'gipfl\\Stream\\' => 13,
+ 'gipfl\\Socket\\' => 13,
+ 'gipfl\\SimpleDaemon\\' => 19,
+ 'gipfl\\ReactUtils\\' => 17,
+ 'gipfl\\Protocol\\NetString\\' => 25,
+ 'gipfl\\Protocol\\JsonRpc\\' => 23,
+ 'gipfl\\Protocol\\Generic\\' => 23,
+ 'gipfl\\Protocol\\Exception\\' => 25,
+ 'gipfl\\Process\\' => 14,
+ 'gipfl\\OpenRpc\\' => 14,
+ 'gipfl\\Log\\' => 10,
+ 'gipfl\\LinuxHealth\\' => 18,
+ 'gipfl\\Json\\' => 11,
+ 'gipfl\\InfluxDb\\' => 15,
+ 'gipfl\\IcingaWeb2\\' => 17,
+ 'gipfl\\IcingaCliDaemon\\' => 22,
+ 'gipfl\\Format\\' => 13,
+ 'gipfl\\Diff\\' => 11,
+ 'gipfl\\DbMigration\\' => 18,
+ 'gipfl\\DataType\\' => 15,
+ 'gipfl\\Curl\\' => 11,
+ 'gipfl\\Cli\\' => 10,
+ 'gipfl\\Calendar\\' => 15,
+ ),
+ 'P' =>
+ array (
+ 'Psr\\Log\\' => 8,
+ ),
+ );
+
+ public static $prefixDirsPsr4 = array (
+ 'gipfl\\ZfDb\\' =>
+ array (
+ 0 => __DIR__ . '/..' . '/gipfl/zfdb/src',
+ ),
+ 'gipfl\\ZfDbStore\\' =>
+ array (
+ 0 => __DIR__ . '/..' . '/gipfl/zfdbstore/src',
+ ),
+ 'gipfl\\Web\\' =>
+ array (
+ 0 => __DIR__ . '/..' . '/gipfl/web/src',
+ ),
+ 'gipfl\\Translation\\' =>
+ array (
+ 0 => __DIR__ . '/..' . '/gipfl/translation/src',
+ ),
+ 'gipfl\\SystemD\\' =>
+ array (
+ 0 => __DIR__ . '/..' . '/gipfl/systemd/src',
+ ),
+ 'gipfl\\Stream\\' =>
+ array (
+ 0 => __DIR__ . '/..' . '/gipfl/stream/src',
+ ),
+ 'gipfl\\Socket\\' =>
+ array (
+ 0 => __DIR__ . '/..' . '/gipfl/socket/src',
+ ),
+ 'gipfl\\SimpleDaemon\\' =>
+ array (
+ 0 => __DIR__ . '/..' . '/gipfl/simple-daemon/src',
+ ),
+ 'gipfl\\ReactUtils\\' =>
+ array (
+ 0 => __DIR__ . '/..' . '/gipfl/react-utils/src',
+ ),
+ 'gipfl\\Protocol\\NetString\\' =>
+ array (
+ 0 => __DIR__ . '/..' . '/gipfl/protocol-netstring/src',
+ ),
+ 'gipfl\\Protocol\\JsonRpc\\' =>
+ array (
+ 0 => __DIR__ . '/..' . '/gipfl/protocol-jsonrpc/src',
+ ),
+ 'gipfl\\Protocol\\Generic\\' =>
+ array (
+ 0 => __DIR__ . '/..' . '/gipfl/protocol/src/Generic',
+ ),
+ 'gipfl\\Protocol\\Exception\\' =>
+ array (
+ 0 => __DIR__ . '/..' . '/gipfl/protocol/src/Exception',
+ ),
+ 'gipfl\\Process\\' =>
+ array (
+ 0 => __DIR__ . '/..' . '/gipfl/process/src',
+ ),
+ 'gipfl\\OpenRpc\\' =>
+ array (
+ 0 => __DIR__ . '/..' . '/gipfl/openrpc/src',
+ ),
+ 'gipfl\\Log\\' =>
+ array (
+ 0 => __DIR__ . '/..' . '/gipfl/log/src',
+ ),
+ 'gipfl\\LinuxHealth\\' =>
+ array (
+ 0 => __DIR__ . '/..' . '/gipfl/linux-health/src',
+ ),
+ 'gipfl\\Json\\' =>
+ array (
+ 0 => __DIR__ . '/..' . '/gipfl/json/src',
+ ),
+ 'gipfl\\InfluxDb\\' =>
+ array (
+ 0 => __DIR__ . '/..' . '/gipfl/influxdb/src',
+ ),
+ 'gipfl\\IcingaWeb2\\' =>
+ array (
+ 0 => __DIR__ . '/..' . '/gipfl/icingaweb2/src',
+ ),
+ 'gipfl\\IcingaCliDaemon\\' =>
+ array (
+ 0 => __DIR__ . '/..' . '/gipfl/icinga-cli-daemon/src',
+ ),
+ 'gipfl\\Format\\' =>
+ array (
+ 0 => __DIR__ . '/..' . '/gipfl/format/src',
+ ),
+ 'gipfl\\Diff\\' =>
+ array (
+ 0 => __DIR__ . '/..' . '/gipfl/diff/src',
+ ),
+ 'gipfl\\DbMigration\\' =>
+ array (
+ 0 => __DIR__ . '/..' . '/gipfl/db-migration/src',
+ ),
+ 'gipfl\\DataType\\' =>
+ array (
+ 0 => __DIR__ . '/..' . '/gipfl/data-type/src',
+ ),
+ 'gipfl\\Curl\\' =>
+ array (
+ 0 => __DIR__ . '/..' . '/gipfl/curl/src',
+ ),
+ 'gipfl\\Cli\\' =>
+ array (
+ 0 => __DIR__ . '/..' . '/gipfl/cli/src',
+ ),
+ 'gipfl\\Calendar\\' =>
+ array (
+ 0 => __DIR__ . '/..' . '/gipfl/calendar/src',
+ ),
+ 'Psr\\Log\\' =>
+ array (
+ 0 => __DIR__ . '/..' . '/psr/log/Psr/Log',
+ ),
+ );
+
+ public static $classMap = array (
+ 'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php',
+ );
+
+ public static function getInitializer(ClassLoader $loader)
+ {
+ return \Closure::bind(function () use ($loader) {
+ $loader->prefixLengthsPsr4 = ComposerStaticInit684800034bdac0af606e9381dda72632::$prefixLengthsPsr4;
+ $loader->prefixDirsPsr4 = ComposerStaticInit684800034bdac0af606e9381dda72632::$prefixDirsPsr4;
+ $loader->classMap = ComposerStaticInit684800034bdac0af606e9381dda72632::$classMap;
+
+ }, null, ClassLoader::class);
+ }
+}
diff --git a/vendor/composer/installed.json b/vendor/composer/installed.json
new file mode 100644
index 0000000..961123c
--- /dev/null
+++ b/vendor/composer/installed.json
@@ -0,0 +1,1350 @@
+{
+ "packages": [
+ {
+ "name": "gipfl/calendar",
+ "version": "v0.3.1",
+ "version_normalized": "0.3.1.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/gipfl/calendar.git",
+ "reference": "fd57d12aa97dd4c2a115e5cae36097ffaed220b8"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/gipfl/calendar/zipball/fd57d12aa97dd4c2a115e5cae36097ffaed220b8",
+ "reference": "fd57d12aa97dd4c2a115e5cae36097ffaed220b8",
+ "shasum": ""
+ },
+ "require": {
+ "gipfl/format": ">=0.3",
+ "gipfl/icingaweb2": ">=0.4.0",
+ "gipfl/translation": ">=0.1.1",
+ "php": ">=5.4.0"
+ },
+ "time": "2023-01-16T14:08:24+00:00",
+ "type": "library",
+ "installation-source": "dist",
+ "autoload": {
+ "psr-4": {
+ "gipfl\\Calendar\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "authors": [
+ {
+ "name": "Thomas Gelf",
+ "email": "thomas@gelf.net"
+ }
+ ],
+ "description": "Calendar Utils",
+ "homepage": "https://github.com/gipfl/calendar",
+ "keywords": [
+ "calendar"
+ ],
+ "support": {
+ "issues": "https://github.com/gipfl/calendar/issues",
+ "source": "https://github.com/gipfl/calendar/tree/v0.3.1"
+ },
+ "install-path": "../gipfl/calendar"
+ },
+ {
+ "name": "gipfl/cli",
+ "version": "v0.6.0",
+ "version_normalized": "0.6.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/gipfl/cli.git",
+ "reference": "536dec1fa605aa98aef32421cd70bf71e46406d0"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/gipfl/cli/zipball/536dec1fa605aa98aef32421cd70bf71e46406d0",
+ "reference": "536dec1fa605aa98aef32421cd70bf71e46406d0",
+ "shasum": ""
+ },
+ "require": {
+ "ext-mbstring": "*",
+ "ext-pcntl": "*",
+ "ext-posix": "*",
+ "php": ">=5.6.0",
+ "react/promise": "^2",
+ "react/stream": ">=1.1"
+ },
+ "require-dev": {
+ "react/child-process": ">=0.6"
+ },
+ "time": "2022-10-07T13:11:48+00:00",
+ "type": "library",
+ "installation-source": "dist",
+ "autoload": {
+ "psr-4": {
+ "gipfl\\Cli\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Thomas Gelf",
+ "email": "thomas@gelf.net"
+ }
+ ],
+ "description": "CLI utilities",
+ "support": {
+ "issues": "https://github.com/gipfl/cli/issues",
+ "source": "https://github.com/gipfl/cli/tree/v0.6.0"
+ },
+ "install-path": "../gipfl/cli"
+ },
+ {
+ "name": "gipfl/curl",
+ "version": "v0.3.0",
+ "version_normalized": "0.3.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/gipfl/curl.git",
+ "reference": "bc1373dff8ab247c734d019d0fe7d94521660b1f"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/gipfl/curl/zipball/bc1373dff8ab247c734d019d0fe7d94521660b1f",
+ "reference": "bc1373dff8ab247c734d019d0fe7d94521660b1f",
+ "shasum": ""
+ },
+ "require": {
+ "ext-curl": "*",
+ "guzzlehttp/psr7": ">=1.6",
+ "php": ">=5.6.3",
+ "psr/http-message": "^1.0",
+ "react/event-loop": ">=1.0",
+ "react/promise": ">=2",
+ "react/stream": ">=1.0"
+ },
+ "time": "2022-10-07T13:34:17+00:00",
+ "type": "library",
+ "installation-source": "dist",
+ "autoload": {
+ "psr-4": {
+ "gipfl\\Curl\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Thomas Gelf",
+ "email": "thomas@gelf.net"
+ }
+ ],
+ "description": "ReactPHP-friendly async CURL abstraction",
+ "support": {
+ "issues": "https://github.com/gipfl/curl/issues",
+ "source": "https://github.com/gipfl/curl/tree/v0.3.0"
+ },
+ "install-path": "../gipfl/curl"
+ },
+ {
+ "name": "gipfl/data-type",
+ "version": "v0.3.0",
+ "version_normalized": "0.3.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/gipfl/data-type.git",
+ "reference": "eb81eaeef93adb15c6c5522154516eae0e2a8e48"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/gipfl/data-type/zipball/eb81eaeef93adb15c6c5522154516eae0e2a8e48",
+ "reference": "eb81eaeef93adb15c6c5522154516eae0e2a8e48",
+ "shasum": ""
+ },
+ "require": {
+ "gipfl/json": ">=0.2.0",
+ "php": ">=5.6.0"
+ },
+ "time": "2022-09-01T07:38:42+00:00",
+ "type": "library",
+ "installation-source": "dist",
+ "autoload": {
+ "psr-4": {
+ "gipfl\\DataType\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Thomas Gelf",
+ "email": "thomas@gelf.net"
+ }
+ ],
+ "description": "Serializable Data Types",
+ "support": {
+ "issues": "https://github.com/gipfl/data-type/issues",
+ "source": "https://github.com/gipfl/data-type/tree/v0.3.0"
+ },
+ "install-path": "../gipfl/data-type"
+ },
+ {
+ "name": "gipfl/db-migration",
+ "version": "v0.1.1",
+ "version_normalized": "0.1.1.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/gipfl/db-migration.git",
+ "reference": "12be94b0c83a5e018c9d3d42fe8e98b4cbe248ee"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/gipfl/db-migration/zipball/12be94b0c83a5e018c9d3d42fe8e98b4cbe248ee",
+ "reference": "12be94b0c83a5e018c9d3d42fe8e98b4cbe248ee",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.6"
+ },
+ "time": "2021-11-11T15:40:11+00:00",
+ "type": "library",
+ "installation-source": "dist",
+ "autoload": {
+ "psr-4": {
+ "gipfl\\DbMigration\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "description": "Simple DB migration helper",
+ "support": {
+ "issues": "https://github.com/gipfl/db-migration/issues",
+ "source": "https://github.com/gipfl/db-migration/tree/v0.1.1"
+ },
+ "install-path": "../gipfl/db-migration"
+ },
+ {
+ "name": "gipfl/diff",
+ "version": "v0.3.0",
+ "version_normalized": "0.3.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/gipfl/diff.git",
+ "reference": "f9decb9cf20735d6cbe6f508aafd8a720922804b"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/gipfl/diff/zipball/f9decb9cf20735d6cbe6f508aafd8a720922804b",
+ "reference": "f9decb9cf20735d6cbe6f508aafd8a720922804b",
+ "shasum": ""
+ },
+ "require": {
+ "ext-mbstring": "*",
+ "ipl/html": ">=0.2",
+ "php": ">=5.6.0"
+ },
+ "time": "2022-04-18T11:39:03+00:00",
+ "type": "library",
+ "installation-source": "dist",
+ "autoload": {
+ "psr-4": {
+ "gipfl\\Diff\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Thomas Gelf",
+ "email": "thomas@gelf.net"
+ }
+ ],
+ "description": "php-diff wrapper supporting ipl/html",
+ "support": {
+ "issues": "https://github.com/gipfl/diff/issues",
+ "source": "https://github.com/gipfl/diff/tree/v0.3.0"
+ },
+ "install-path": "../gipfl/diff"
+ },
+ {
+ "name": "gipfl/format",
+ "version": "v0.4.0",
+ "version_normalized": "0.4.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/gipfl/format.git",
+ "reference": "87bd240f80baac73d18aeaf11ef389d94c56c8a0"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/gipfl/format/zipball/87bd240f80baac73d18aeaf11ef389d94c56c8a0",
+ "reference": "87bd240f80baac73d18aeaf11ef389d94c56c8a0",
+ "shasum": ""
+ },
+ "require": {
+ "ext-intl": "*",
+ "php": ">=5.6.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^9.3 || ^7.5 || ^6.5 || ^5.7",
+ "squizlabs/php_codesniffer": "^3.6"
+ },
+ "time": "2023-01-16T11:46:49+00:00",
+ "type": "library",
+ "installation-source": "dist",
+ "autoload": {
+ "psr-4": {
+ "gipfl\\Format\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Thomas Gelf",
+ "email": "thomas@gelf.net"
+ }
+ ],
+ "description": "Arbitrary collection of Format helpers",
+ "support": {
+ "issues": "https://github.com/gipfl/format/issues",
+ "source": "https://github.com/gipfl/format/tree/v0.4.0"
+ },
+ "install-path": "../gipfl/format"
+ },
+ {
+ "name": "gipfl/icinga-bundles",
+ "version": "v0.7.0",
+ "version_normalized": "0.7.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/gipfl/icinga-bundles.git",
+ "reference": "d28e12218c07174795e07d7ad235d3fac2a6be53"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/gipfl/icinga-bundles/zipball/d28e12218c07174795e07d7ad235d3fac2a6be53",
+ "reference": "d28e12218c07174795e07d7ad235d3fac2a6be53",
+ "shasum": ""
+ },
+ "require": {
+ "ext-json": "*"
+ },
+ "replace": {
+ "clue/block-react": "v1.4.0",
+ "clue/buzz-react": "v2.7.0",
+ "clue/connection-manager-extra": "v1.1.0",
+ "clue/http-proxy-react": "v1.5.0",
+ "clue/mq-react": "v1.2.0",
+ "clue/redis-protocol": "v0.3.1",
+ "clue/redis-react": "v2.3.0",
+ "clue/soap-react": "v1.0.0",
+ "clue/socket-raw": "v1.4.1",
+ "clue/socks-react": "v1.1.0",
+ "clue/stdio-react": "v2.3.0",
+ "clue/term-react": "v1.2.0",
+ "clue/utf8-react": "v1.1.0",
+ "evenement/evenement": "v2.1.0",
+ "guzzlehttp/psr7": "1.6.1",
+ "ipl/html": "v0.3.0",
+ "ipl/orm": "v0.1.0",
+ "ipl/sql": "v0.1.0",
+ "ipl/stdlib": "v0.5.0",
+ "ipl/validator": "v0.1.0",
+ "ipl/web": "v0.1.0",
+ "paragonie/random_compat": "v2.0.18",
+ "predis/predis": "v1.1.6",
+ "psr/http-message": "1.0.1",
+ "ralouphie/getallheaders": "3.0.3",
+ "ramsey/uuid": "3.9.3",
+ "react/cache": "v1.1.0",
+ "react/child-process": "v0.6.1",
+ "react/datagram": "v1.5.0",
+ "react/dns": "v1.4.0",
+ "react/event-loop": "v1.1.1",
+ "react/http": "v1.1.0",
+ "react/http-client": "v0.5.10",
+ "react/promise": "v2.8.0",
+ "react/promise-stream": "v1.2.0",
+ "react/promise-timer": "v1.6.0",
+ "react/socket": "v1.6.0",
+ "react/stream": "v1.1.1",
+ "ringcentral/psr7": "1.3.0",
+ "symfony/polyfill-ctype": "v1.18.1"
+ },
+ "time": "2020-10-08T17:20:30+00:00",
+ "type": "metapackage",
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Thomas Gelf",
+ "email": "thomas@gelf.net"
+ }
+ ],
+ "description": "Provides (replaces) all packages provided by ipl and reactbundle",
+ "support": {
+ "issues": "https://github.com/gipfl/icinga-bundles/issues",
+ "source": "https://github.com/gipfl/icinga-bundles/tree/v0.7.0"
+ },
+ "install-path": null
+ },
+ {
+ "name": "gipfl/icinga-cli-daemon",
+ "version": "v0.3.2",
+ "version_normalized": "0.3.2.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/gipfl/icinga-cli-daemon.git",
+ "reference": "19e1b203108fda37de39488fd8ff7ea392a42f03"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/gipfl/icinga-cli-daemon/zipball/19e1b203108fda37de39488fd8ff7ea392a42f03",
+ "reference": "19e1b203108fda37de39488fd8ff7ea392a42f03",
+ "shasum": ""
+ },
+ "require": {
+ "ext-posix": "*",
+ "gipfl/cli": ">=0.5.0",
+ "php": ">=5.6.3",
+ "react/event-loop": ">=1.1",
+ "react/promise": ">=2.7"
+ },
+ "time": "2021-10-13T08:49:48+00:00",
+ "type": "library",
+ "installation-source": "dist",
+ "autoload": {
+ "psr-4": {
+ "gipfl\\IcingaCliDaemon\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "description": "Helpers for Icinga CLI Daemons",
+ "homepage": "https://github.com/gipfl/icinga-cli-daemon",
+ "support": {
+ "issues": "https://github.com/gipfl/icinga-cli-daemon/issues",
+ "source": "https://github.com/gipfl/icinga-cli-daemon/tree/v0.3.2"
+ },
+ "install-path": "../gipfl/icinga-cli-daemon"
+ },
+ {
+ "name": "gipfl/icingaweb2",
+ "version": "v0.10.0",
+ "version_normalized": "0.10.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/gipfl/icingaweb2.git",
+ "reference": "36a2ca4739a0de7ffc365e625bcb81979aca3435"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/gipfl/icingaweb2/zipball/36a2ca4739a0de7ffc365e625bcb81979aca3435",
+ "reference": "36a2ca4739a0de7ffc365e625bcb81979aca3435",
+ "shasum": ""
+ },
+ "require": {
+ "ext-ctype": "*",
+ "gipfl/format": ">=0.3",
+ "gipfl/translation": ">=0.1",
+ "ipl/html": ">=0.2.1",
+ "php": ">=5.6"
+ },
+ "time": "2023-01-16T13:50:24+00:00",
+ "type": "library",
+ "installation-source": "dist",
+ "autoload": {
+ "psr-4": {
+ "gipfl\\IcingaWeb2\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "description": "Helpers and glue for Icinga Web 2",
+ "homepage": "https://github.com/gipfl/icingaweb2",
+ "support": {
+ "issues": "https://github.com/gipfl/icingaweb2/issues",
+ "source": "https://github.com/gipfl/icingaweb2/tree/v0.10.0"
+ },
+ "install-path": "../gipfl/icingaweb2"
+ },
+ {
+ "name": "gipfl/influxdb",
+ "version": "v0.5.0",
+ "version_normalized": "0.5.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/gipfl/influxdb.git",
+ "reference": "0b7b46ceecb1a6c9fcd738973f773d23d97b374a"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/gipfl/influxdb/zipball/0b7b46ceecb1a6c9fcd738973f773d23d97b374a",
+ "reference": "0b7b46ceecb1a6c9fcd738973f773d23d97b374a",
+ "shasum": ""
+ },
+ "require": {
+ "ext-ctype": "*",
+ "ext-pcntl": "*",
+ "gipfl/curl": ">=0.1.1",
+ "gipfl/json": ">=0.2",
+ "php": ">=5.6.0",
+ "react/event-loop": ">=1.1"
+ },
+ "time": "2022-10-07T13:57:22+00:00",
+ "type": "library",
+ "installation-source": "dist",
+ "autoload": {
+ "psr-4": {
+ "gipfl\\InfluxDb\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Thomas Gelf",
+ "email": "thomas@gelf.net"
+ }
+ ],
+ "description": "InfluxDB client library",
+ "support": {
+ "issues": "https://github.com/gipfl/influxdb/issues",
+ "source": "https://github.com/gipfl/influxdb/tree/v0.5.0"
+ },
+ "install-path": "../gipfl/influxdb"
+ },
+ {
+ "name": "gipfl/json",
+ "version": "v0.2.0",
+ "version_normalized": "0.2.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/gipfl/json.git",
+ "reference": "5635790a19150beeece9edc67e82ecddc52ff4fb"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/gipfl/json/zipball/5635790a19150beeece9edc67e82ecddc52ff4fb",
+ "reference": "5635790a19150beeece9edc67e82ecddc52ff4fb",
+ "shasum": ""
+ },
+ "require": {
+ "ext-json": "*"
+ },
+ "time": "2021-11-18T12:48:06+00:00",
+ "type": "library",
+ "installation-source": "dist",
+ "autoload": {
+ "psr-4": {
+ "gipfl\\Json\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Thomas Gelf",
+ "email": "thomas@gelf.net"
+ }
+ ],
+ "description": "Simple JSON-related helper classes and interfaces",
+ "support": {
+ "issues": "https://github.com/gipfl/json/issues",
+ "source": "https://github.com/gipfl/json/tree/v0.2.0"
+ },
+ "install-path": "../gipfl/json"
+ },
+ {
+ "name": "gipfl/linux-health",
+ "version": "v0.2.0",
+ "version_normalized": "0.2.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/gipfl/linux-health.git",
+ "reference": "4d212535565f353f88c9eef3ecb54e20c40b8b37"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/gipfl/linux-health/zipball/4d212535565f353f88c9eef3ecb54e20c40b8b37",
+ "reference": "4d212535565f353f88c9eef3ecb54e20c40b8b37",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.4.0"
+ },
+ "time": "2020-07-30T09:11:27+00:00",
+ "type": "library",
+ "installation-source": "dist",
+ "autoload": {
+ "psr-4": {
+ "gipfl\\LinuxHealth\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Thomas Gelf",
+ "email": "thomas@gelf.net"
+ }
+ ],
+ "description": "Various little Linux Health based classes",
+ "support": {
+ "issues": "https://github.com/gipfl/linux-health/issues",
+ "source": "https://github.com/gipfl/linux-health/tree/master"
+ },
+ "install-path": "../gipfl/linux-health"
+ },
+ {
+ "name": "gipfl/log",
+ "version": "v0.7.0",
+ "version_normalized": "0.7.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/gipfl/log.git",
+ "reference": "df770bdc9c09b0428831345af7ce7d45a0c3a1bb"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/gipfl/log/zipball/df770bdc9c09b0428831345af7ce7d45a0c3a1bb",
+ "reference": "df770bdc9c09b0428831345af7ce7d45a0c3a1bb",
+ "shasum": ""
+ },
+ "require": {
+ "ext-iconv": "*",
+ "php": ">=5.6.0",
+ "psr/log": "^1"
+ },
+ "require-dev": {
+ "gipfl/protocol-jsonrpc": ">=0.2",
+ "gipfl/systemd": ">=0.3"
+ },
+ "time": "2021-11-11T15:39:23+00:00",
+ "type": "library",
+ "installation-source": "dist",
+ "autoload": {
+ "psr-4": {
+ "gipfl\\Log\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Thomas Gelf",
+ "email": "thomas@gelf.net"
+ }
+ ],
+ "description": "Lightweight PSR-3 compatible logger",
+ "support": {
+ "issues": "https://github.com/gipfl/log/issues",
+ "source": "https://github.com/gipfl/log/tree/v0.7.0"
+ },
+ "install-path": "../gipfl/log"
+ },
+ {
+ "name": "gipfl/openrpc",
+ "version": "v0.2.1",
+ "version_normalized": "0.2.1.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/gipfl/openrpc.git",
+ "reference": "a29b8da2cdaf7d757d86aa0fe6d719cf68fb95ad"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/gipfl/openrpc/zipball/a29b8da2cdaf7d757d86aa0fe6d719cf68fb95ad",
+ "reference": "a29b8da2cdaf7d757d86aa0fe6d719cf68fb95ad",
+ "shasum": ""
+ },
+ "require": {
+ "ext-json": "*",
+ "php": ">=5.6.0"
+ },
+ "time": "2021-11-29T13:53:50+00:00",
+ "type": "library",
+ "installation-source": "dist",
+ "autoload": {
+ "psr-4": {
+ "gipfl\\OpenRpc\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Thomas Gelf",
+ "email": "thomas@gelf.net"
+ }
+ ],
+ "description": "OpenRPC Connection implementation",
+ "support": {
+ "issues": "https://github.com/gipfl/openrpc/issues",
+ "source": "https://github.com/gipfl/openrpc/tree/v0.2.1"
+ },
+ "install-path": "../gipfl/openrpc"
+ },
+ {
+ "name": "gipfl/process",
+ "version": "v0.4.0",
+ "version_normalized": "0.4.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/gipfl/process.git",
+ "reference": "ddbc3971cf56b35ce17d7cc8aaf241e87a792938"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/gipfl/process/zipball/ddbc3971cf56b35ce17d7cc8aaf241e87a792938",
+ "reference": "ddbc3971cf56b35ce17d7cc8aaf241e87a792938",
+ "shasum": ""
+ },
+ "require": {
+ "ext-json": "*",
+ "gipfl/json": ">=0.1",
+ "gipfl/linux-health": ">=0.2",
+ "php": ">=5.6.0",
+ "react/child-process": ">=0.6",
+ "react/promise": "^2"
+ },
+ "time": "2022-09-02T09:03:58+00:00",
+ "type": "library",
+ "installation-source": "dist",
+ "autoload": {
+ "psr-4": {
+ "gipfl\\Process\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Thomas Gelf",
+ "email": "thomas@gelf.net"
+ }
+ ],
+ "description": "Process-related utility classes",
+ "support": {
+ "issues": "https://github.com/gipfl/process/issues",
+ "source": "https://github.com/gipfl/process/tree/v0.4.0"
+ },
+ "install-path": "../gipfl/process"
+ },
+ {
+ "name": "gipfl/protocol",
+ "version": "v0.2.0",
+ "version_normalized": "0.2.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/gipfl/protocol.git",
+ "reference": "bf1a0e160fe6f74c6be60be569c466d57551e73d"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/gipfl/protocol/zipball/bf1a0e160fe6f74c6be60be569c466d57551e73d",
+ "reference": "bf1a0e160fe6f74c6be60be569c466d57551e73d",
+ "shasum": ""
+ },
+ "require": {
+ "evenement/evenement": "^2",
+ "php": ">=5.4.0",
+ "react/stream": "^1.0"
+ },
+ "time": "2020-09-22T04:22:23+00:00",
+ "type": "library",
+ "installation-source": "dist",
+ "autoload": {
+ "psr-4": {
+ "gipfl\\Protocol\\Generic\\": "src/Generic",
+ "gipfl\\Protocol\\Exception\\": "src/Exception"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Thomas Gelf",
+ "email": "thomas@gelf.net"
+ }
+ ],
+ "description": "Base library for some network protocol implementations",
+ "support": {
+ "issues": "https://github.com/gipfl/protocol/issues",
+ "source": "https://github.com/gipfl/protocol/tree/v0.2.0"
+ },
+ "install-path": "../gipfl/protocol"
+ },
+ {
+ "name": "gipfl/protocol-jsonrpc",
+ "version": "v0.11.0",
+ "version_normalized": "0.11.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/gipfl/protocol-jsonrpc.git",
+ "reference": "30b0241c67019746fce0b464543d8f936bdb9b68"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/gipfl/protocol-jsonrpc/zipball/30b0241c67019746fce0b464543d8f936bdb9b68",
+ "reference": "30b0241c67019746fce0b464543d8f936bdb9b68",
+ "shasum": ""
+ },
+ "require": {
+ "ext-json": "*",
+ "gipfl/json": ">=0.1",
+ "gipfl/openrpc": "^0.2.1",
+ "gipfl/protocol": ">=0.2",
+ "php": ">=5.6.0",
+ "psr/log": ">=1.1",
+ "react/promise": ">=2.7",
+ "react/stream": ">=1.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^9.3 || ^7.5 || ^6.5 || ^5.7",
+ "squizlabs/php_codesniffer": "^3.6"
+ },
+ "time": "2023-01-16T13:58:38+00:00",
+ "type": "library",
+ "installation-source": "dist",
+ "autoload": {
+ "psr-4": {
+ "gipfl\\Protocol\\JsonRpc\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Thomas Gelf",
+ "email": "thomas@gelf.net"
+ }
+ ],
+ "description": "JsonRPC Connection implementation",
+ "support": {
+ "issues": "https://github.com/gipfl/protocol-jsonrpc/issues",
+ "source": "https://github.com/gipfl/protocol-jsonrpc/tree/v0.11.0"
+ },
+ "install-path": "../gipfl/protocol-jsonrpc"
+ },
+ {
+ "name": "gipfl/protocol-netstring",
+ "version": "v0.1.1",
+ "version_normalized": "0.1.1.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/gipfl/protocol-netstring.git",
+ "reference": "c0021ea9d69883760ac176121041a05c9a51ca11"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/gipfl/protocol-netstring/zipball/c0021ea9d69883760ac176121041a05c9a51ca11",
+ "reference": "c0021ea9d69883760ac176121041a05c9a51ca11",
+ "shasum": ""
+ },
+ "require": {
+ "ext-ctype": "*",
+ "gipfl/protocol": ">=0.2",
+ "php": ">=5.4.0"
+ },
+ "time": "2020-10-08T23:38:20+00:00",
+ "type": "library",
+ "installation-source": "dist",
+ "autoload": {
+ "psr-4": {
+ "gipfl\\Protocol\\NetString\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Thomas Gelf",
+ "email": "thomas@gelf.net"
+ }
+ ],
+ "description": "Simple NetString stream wrapper",
+ "support": {
+ "issues": "https://github.com/gipfl/protocol-netstring/issues",
+ "source": "https://github.com/gipfl/protocol-netstring/tree/v0.1.1"
+ },
+ "install-path": "../gipfl/protocol-netstring"
+ },
+ {
+ "name": "gipfl/react-utils",
+ "version": "v0.3.0",
+ "version_normalized": "0.3.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/gipfl/react-utils.git",
+ "reference": "37ff2daa4bcc36aced4cc9df5198caad519d7bdb"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/gipfl/react-utils/zipball/37ff2daa4bcc36aced4cc9df5198caad519d7bdb",
+ "reference": "37ff2daa4bcc36aced4cc9df5198caad519d7bdb",
+ "shasum": ""
+ },
+ "require": {
+ "gipfl/log": ">=0.1",
+ "php": ">=5.6.0"
+ },
+ "time": "2021-10-29T12:17:04+00:00",
+ "type": "library",
+ "installation-source": "dist",
+ "autoload": {
+ "psr-4": {
+ "gipfl\\ReactUtils\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Thomas Gelf",
+ "email": "thomas@gelf.net"
+ }
+ ],
+ "description": "Useful ReactPHP-related helper classes and methods",
+ "support": {
+ "issues": "https://github.com/gipfl/react-utils/issues",
+ "source": "https://github.com/gipfl/react-utils/tree/v0.3.0"
+ },
+ "install-path": "../gipfl/react-utils"
+ },
+ {
+ "name": "gipfl/simple-daemon",
+ "version": "v0.6.0",
+ "version_normalized": "0.6.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/gipfl/simple-daemon.git",
+ "reference": "b7cd3be54c171d20c6692dbb331a3ae6fe90f276"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/gipfl/simple-daemon/zipball/b7cd3be54c171d20c6692dbb331a3ae6fe90f276",
+ "reference": "b7cd3be54c171d20c6692dbb331a3ae6fe90f276",
+ "shasum": ""
+ },
+ "require": {
+ "evenement/evenement": "*",
+ "ext-pcntl": "*",
+ "gipfl/cli": ">=0.5",
+ "gipfl/json": ">=0.1",
+ "gipfl/systemd": ">=0.3",
+ "php": ">=5.6.0",
+ "psr/log": ">=1.0",
+ "react/event-loop": ">=1.1",
+ "react/promise": "^2",
+ "react/promise-timer": ">=1.5"
+ },
+ "time": "2022-10-07T14:08:26+00:00",
+ "type": "library",
+ "installation-source": "dist",
+ "autoload": {
+ "psr-4": {
+ "gipfl\\SimpleDaemon\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Thomas Gelf",
+ "email": "thomas@gelf.net"
+ }
+ ],
+ "description": "Run a simple daemon",
+ "support": {
+ "issues": "https://github.com/gipfl/simple-daemon/issues",
+ "source": "https://github.com/gipfl/simple-daemon/tree/v0.6.0"
+ },
+ "install-path": "../gipfl/simple-daemon"
+ },
+ {
+ "name": "gipfl/socket",
+ "version": "v0.4.0",
+ "version_normalized": "0.4.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/gipfl/socket.git",
+ "reference": "1a2c78709e0bcf7de2469860fbfa807a4bcee555"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/gipfl/socket/zipball/1a2c78709e0bcf7de2469860fbfa807a4bcee555",
+ "reference": "1a2c78709e0bcf7de2469860fbfa807a4bcee555",
+ "shasum": ""
+ },
+ "require": {
+ "evenement/evenement": ">=2.0",
+ "ext-posix": "*",
+ "ext-sockets": "*",
+ "gipfl/json": ">=0.1",
+ "php": ">=5.6.0",
+ "react/event-loop": ">=1.0",
+ "react/socket": ">=1.0"
+ },
+ "time": "2021-12-02T02:23:18+00:00",
+ "type": "library",
+ "installation-source": "dist",
+ "autoload": {
+ "psr-4": {
+ "gipfl\\Socket\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Thomas Gelf",
+ "email": "thomas@gelf.net"
+ }
+ ],
+ "description": "Helpful ReactPHP socket utility classes",
+ "support": {
+ "issues": "https://github.com/gipfl/socket/issues",
+ "source": "https://github.com/gipfl/socket/tree/v0.4.0"
+ },
+ "install-path": "../gipfl/socket"
+ },
+ {
+ "name": "gipfl/stream",
+ "version": "v0.2.0",
+ "version_normalized": "0.2.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/gipfl/stream.git",
+ "reference": "ae0f8f4fd47a6dcc57885b232e239b0ba82889a3"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/gipfl/stream/zipball/ae0f8f4fd47a6dcc57885b232e239b0ba82889a3",
+ "reference": "ae0f8f4fd47a6dcc57885b232e239b0ba82889a3",
+ "shasum": ""
+ },
+ "require": {
+ "react/event-loop": ">=1.0",
+ "react/stream": ">=1.0"
+ },
+ "require-dev": {
+ "gipfl/test": ">=0.1.1",
+ "phpunit/phpunit": "^9.3 || ^7.5 || ^6.5 || ^5.7",
+ "squizlabs/php_codesniffer": "^3.6"
+ },
+ "time": "2021-11-27T12:17:36+00:00",
+ "type": "library",
+ "installation-source": "dist",
+ "autoload": {
+ "psr-4": {
+ "gipfl\\Stream\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Thomas Gelf",
+ "email": "thomas@gelf.net"
+ }
+ ],
+ "description": "Helpful ReactPHP stream utility classes",
+ "support": {
+ "issues": "https://github.com/gipfl/stream/issues",
+ "source": "https://github.com/gipfl/stream/tree/v0.2.0"
+ },
+ "install-path": "../gipfl/stream"
+ },
+ {
+ "name": "gipfl/systemd",
+ "version": "v0.3.0",
+ "version_normalized": "0.3.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/gipfl/systemd.git",
+ "reference": "ccfabb352a9ea9fd33d4cb426bc3793a651bf466"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/gipfl/systemd/zipball/ccfabb352a9ea9fd33d4cb426bc3793a651bf466",
+ "reference": "ccfabb352a9ea9fd33d4cb426bc3793a651bf466",
+ "shasum": ""
+ },
+ "require": {
+ "ext-posix": "*",
+ "ext-sockets": "*",
+ "php": ">=5.6.3",
+ "react/event-loop": "^1.0"
+ },
+ "time": "2020-11-13T22:35:11+00:00",
+ "type": "library",
+ "installation-source": "dist",
+ "autoload": {
+ "psr-4": {
+ "gipfl\\SystemD\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Thomas Gelf",
+ "email": "thomas@gelf.net"
+ }
+ ],
+ "description": "SystemD-related library",
+ "support": {
+ "issues": "https://github.com/gipfl/systemd/issues",
+ "source": "https://github.com/gipfl/systemd/tree/v0.3.0"
+ },
+ "install-path": "../gipfl/systemd"
+ },
+ {
+ "name": "gipfl/translation",
+ "version": "v0.1.1",
+ "version_normalized": "0.1.1.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/gipfl/translation.git",
+ "reference": "e974e6a2f0b524678b6554ee0046b5dd5f70a25d"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/gipfl/translation/zipball/e974e6a2f0b524678b6554ee0046b5dd5f70a25d",
+ "reference": "e974e6a2f0b524678b6554ee0046b5dd5f70a25d",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.4.0"
+ },
+ "time": "2019-09-17T20:36:20+00:00",
+ "type": "library",
+ "installation-source": "dist",
+ "autoload": {
+ "psr-4": {
+ "gipfl\\Translation\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Thomas Gelf",
+ "email": "thomas@gelf.net"
+ }
+ ],
+ "description": "Translation helpers",
+ "support": {
+ "issues": "https://github.com/gipfl/translation/issues",
+ "source": "https://github.com/gipfl/translation/tree/master"
+ },
+ "install-path": "../gipfl/translation"
+ },
+ {
+ "name": "gipfl/web",
+ "version": "v0.11.0",
+ "version_normalized": "0.11.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/gipfl/web.git",
+ "reference": "d331fe195f7bab08561ee015ba56edeb334a1533"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/gipfl/web/zipball/d331fe195f7bab08561ee015ba56edeb334a1533",
+ "reference": "d331fe195f7bab08561ee015ba56edeb334a1533",
+ "shasum": ""
+ },
+ "require": {
+ "gipfl/translation": ">=0.1.1",
+ "ipl/html": ">=0.3",
+ "php": ">=5.6.0"
+ },
+ "time": "2022-09-01T07:25:09+00:00",
+ "type": "library",
+ "installation-source": "dist",
+ "autoload": {
+ "psr-4": {
+ "gipfl\\Web\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Thomas Gelf",
+ "email": "thomas@gelf.net"
+ }
+ ],
+ "description": "Various web widgets",
+ "support": {
+ "issues": "https://github.com/gipfl/web/issues",
+ "source": "https://github.com/gipfl/web/tree/v0.11.0"
+ },
+ "install-path": "../gipfl/web"
+ },
+ {
+ "name": "gipfl/zfdb",
+ "version": "v0.4.0",
+ "version_normalized": "0.4.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/gipfl/zfdb.git",
+ "reference": "beb7ceabc80e8c81f0e237afcfb064a67e1432d6"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/gipfl/zfdb/zipball/beb7ceabc80e8c81f0e237afcfb064a67e1432d6",
+ "reference": "beb7ceabc80e8c81f0e237afcfb064a67e1432d6",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.4"
+ },
+ "time": "2022-08-30T13:10:26+00:00",
+ "type": "library",
+ "installation-source": "dist",
+ "autoload": {
+ "psr-4": {
+ "gipfl\\ZfDb\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "description": "Zend_Db from Zend Framework 1. For compatibility reasons only",
+ "support": {
+ "issues": "https://github.com/gipfl/zfdb/issues",
+ "source": "https://github.com/gipfl/zfdb/tree/v0.4.0"
+ },
+ "install-path": "../gipfl/zfdb"
+ },
+ {
+ "name": "gipfl/zfdbstore",
+ "version": "v0.3.0",
+ "version_normalized": "0.3.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/gipfl/zfdbstore.git",
+ "reference": "5296226865a0c053f29f57a1bccd50b17cab5248"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/gipfl/zfdbstore/zipball/5296226865a0c053f29f57a1bccd50b17cab5248",
+ "reference": "5296226865a0c053f29f57a1bccd50b17cab5248",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.6"
+ },
+ "time": "2023-01-16T13:54:52+00:00",
+ "type": "library",
+ "installation-source": "dist",
+ "autoload": {
+ "psr-4": {
+ "gipfl\\ZfDbStore\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "description": "Storable class helpers for ZfDb",
+ "support": {
+ "issues": "https://github.com/gipfl/zfdbstore/issues",
+ "source": "https://github.com/gipfl/zfdbstore/tree/v0.3.0"
+ },
+ "install-path": "../gipfl/zfdbstore"
+ },
+ {
+ "name": "psr/log",
+ "version": "1.1.4",
+ "version_normalized": "1.1.4.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-fig/log.git",
+ "reference": "d49695b909c3b7628b6289db5479a1c204601f11"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-fig/log/zipball/d49695b909c3b7628b6289db5479a1c204601f11",
+ "reference": "d49695b909c3b7628b6289db5479a1c204601f11",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3.0"
+ },
+ "time": "2021-05-03T11:20:27+00:00",
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.1.x-dev"
+ }
+ },
+ "installation-source": "dist",
+ "autoload": {
+ "psr-4": {
+ "Psr\\Log\\": "Psr/Log/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "PHP-FIG",
+ "homepage": "https://www.php-fig.org/"
+ }
+ ],
+ "description": "Common interface for logging libraries",
+ "homepage": "https://github.com/php-fig/log",
+ "keywords": [
+ "log",
+ "psr",
+ "psr-3"
+ ],
+ "support": {
+ "source": "https://github.com/php-fig/log/tree/1.1.4"
+ },
+ "install-path": "../psr/log"
+ }
+ ],
+ "dev": true,
+ "dev-package-names": []
+}
diff --git a/vendor/composer/installed.php b/vendor/composer/installed.php
new file mode 100644
index 0000000..f7e4184
--- /dev/null
+++ b/vendor/composer/installed.php
@@ -0,0 +1,524 @@
+<?php return array(
+ 'root' => array(
+ 'pretty_version' => 'dev-master',
+ 'version' => 'dev-master',
+ 'type' => 'library',
+ 'install_path' => __DIR__ . '/../../',
+ 'aliases' => array(),
+ 'reference' => '03cc0aac078de25564d09c13ae7de6f0ce9c1b3b',
+ 'name' => 'icinga/incubator',
+ 'dev' => true,
+ ),
+ 'versions' => array(
+ 'clue/block-react' => array(
+ 'dev_requirement' => false,
+ 'replaced' => array(
+ 0 => 'v1.4.0',
+ ),
+ ),
+ 'clue/buzz-react' => array(
+ 'dev_requirement' => false,
+ 'replaced' => array(
+ 0 => 'v2.7.0',
+ ),
+ ),
+ 'clue/connection-manager-extra' => array(
+ 'dev_requirement' => false,
+ 'replaced' => array(
+ 0 => 'v1.1.0',
+ ),
+ ),
+ 'clue/http-proxy-react' => array(
+ 'dev_requirement' => false,
+ 'replaced' => array(
+ 0 => 'v1.5.0',
+ ),
+ ),
+ 'clue/mq-react' => array(
+ 'dev_requirement' => false,
+ 'replaced' => array(
+ 0 => 'v1.2.0',
+ ),
+ ),
+ 'clue/redis-protocol' => array(
+ 'dev_requirement' => false,
+ 'replaced' => array(
+ 0 => 'v0.3.1',
+ ),
+ ),
+ 'clue/redis-react' => array(
+ 'dev_requirement' => false,
+ 'replaced' => array(
+ 0 => 'v2.3.0',
+ ),
+ ),
+ 'clue/soap-react' => array(
+ 'dev_requirement' => false,
+ 'replaced' => array(
+ 0 => 'v1.0.0',
+ ),
+ ),
+ 'clue/socket-raw' => array(
+ 'dev_requirement' => false,
+ 'replaced' => array(
+ 0 => 'v1.4.1',
+ ),
+ ),
+ 'clue/socks-react' => array(
+ 'dev_requirement' => false,
+ 'replaced' => array(
+ 0 => 'v1.1.0',
+ ),
+ ),
+ 'clue/stdio-react' => array(
+ 'dev_requirement' => false,
+ 'replaced' => array(
+ 0 => 'v2.3.0',
+ ),
+ ),
+ 'clue/term-react' => array(
+ 'dev_requirement' => false,
+ 'replaced' => array(
+ 0 => 'v1.2.0',
+ ),
+ ),
+ 'clue/utf8-react' => array(
+ 'dev_requirement' => false,
+ 'replaced' => array(
+ 0 => 'v1.1.0',
+ ),
+ ),
+ 'evenement/evenement' => array(
+ 'dev_requirement' => false,
+ 'replaced' => array(
+ 0 => 'v2.1.0',
+ ),
+ ),
+ 'gipfl/calendar' => array(
+ 'pretty_version' => 'v0.3.1',
+ 'version' => '0.3.1.0',
+ 'type' => 'library',
+ 'install_path' => __DIR__ . '/../gipfl/calendar',
+ 'aliases' => array(),
+ 'reference' => 'fd57d12aa97dd4c2a115e5cae36097ffaed220b8',
+ 'dev_requirement' => false,
+ ),
+ 'gipfl/cli' => array(
+ 'pretty_version' => 'v0.6.0',
+ 'version' => '0.6.0.0',
+ 'type' => 'library',
+ 'install_path' => __DIR__ . '/../gipfl/cli',
+ 'aliases' => array(),
+ 'reference' => '536dec1fa605aa98aef32421cd70bf71e46406d0',
+ 'dev_requirement' => false,
+ ),
+ 'gipfl/curl' => array(
+ 'pretty_version' => 'v0.3.0',
+ 'version' => '0.3.0.0',
+ 'type' => 'library',
+ 'install_path' => __DIR__ . '/../gipfl/curl',
+ 'aliases' => array(),
+ 'reference' => 'bc1373dff8ab247c734d019d0fe7d94521660b1f',
+ 'dev_requirement' => false,
+ ),
+ 'gipfl/data-type' => array(
+ 'pretty_version' => 'v0.3.0',
+ 'version' => '0.3.0.0',
+ 'type' => 'library',
+ 'install_path' => __DIR__ . '/../gipfl/data-type',
+ 'aliases' => array(),
+ 'reference' => 'eb81eaeef93adb15c6c5522154516eae0e2a8e48',
+ 'dev_requirement' => false,
+ ),
+ 'gipfl/db-migration' => array(
+ 'pretty_version' => 'v0.1.1',
+ 'version' => '0.1.1.0',
+ 'type' => 'library',
+ 'install_path' => __DIR__ . '/../gipfl/db-migration',
+ 'aliases' => array(),
+ 'reference' => '12be94b0c83a5e018c9d3d42fe8e98b4cbe248ee',
+ 'dev_requirement' => false,
+ ),
+ 'gipfl/diff' => array(
+ 'pretty_version' => 'v0.3.0',
+ 'version' => '0.3.0.0',
+ 'type' => 'library',
+ 'install_path' => __DIR__ . '/../gipfl/diff',
+ 'aliases' => array(),
+ 'reference' => 'f9decb9cf20735d6cbe6f508aafd8a720922804b',
+ 'dev_requirement' => false,
+ ),
+ 'gipfl/format' => array(
+ 'pretty_version' => 'v0.4.0',
+ 'version' => '0.4.0.0',
+ 'type' => 'library',
+ 'install_path' => __DIR__ . '/../gipfl/format',
+ 'aliases' => array(),
+ 'reference' => '87bd240f80baac73d18aeaf11ef389d94c56c8a0',
+ 'dev_requirement' => false,
+ ),
+ 'gipfl/icinga-bundles' => array(
+ 'pretty_version' => 'v0.7.0',
+ 'version' => '0.7.0.0',
+ 'type' => 'metapackage',
+ 'install_path' => NULL,
+ 'aliases' => array(),
+ 'reference' => 'd28e12218c07174795e07d7ad235d3fac2a6be53',
+ 'dev_requirement' => false,
+ ),
+ 'gipfl/icinga-cli-daemon' => array(
+ 'pretty_version' => 'v0.3.2',
+ 'version' => '0.3.2.0',
+ 'type' => 'library',
+ 'install_path' => __DIR__ . '/../gipfl/icinga-cli-daemon',
+ 'aliases' => array(),
+ 'reference' => '19e1b203108fda37de39488fd8ff7ea392a42f03',
+ 'dev_requirement' => false,
+ ),
+ 'gipfl/icingaweb2' => array(
+ 'pretty_version' => 'v0.10.0',
+ 'version' => '0.10.0.0',
+ 'type' => 'library',
+ 'install_path' => __DIR__ . '/../gipfl/icingaweb2',
+ 'aliases' => array(),
+ 'reference' => '36a2ca4739a0de7ffc365e625bcb81979aca3435',
+ 'dev_requirement' => false,
+ ),
+ 'gipfl/influxdb' => array(
+ 'pretty_version' => 'v0.5.0',
+ 'version' => '0.5.0.0',
+ 'type' => 'library',
+ 'install_path' => __DIR__ . '/../gipfl/influxdb',
+ 'aliases' => array(),
+ 'reference' => '0b7b46ceecb1a6c9fcd738973f773d23d97b374a',
+ 'dev_requirement' => false,
+ ),
+ 'gipfl/json' => array(
+ 'pretty_version' => 'v0.2.0',
+ 'version' => '0.2.0.0',
+ 'type' => 'library',
+ 'install_path' => __DIR__ . '/../gipfl/json',
+ 'aliases' => array(),
+ 'reference' => '5635790a19150beeece9edc67e82ecddc52ff4fb',
+ 'dev_requirement' => false,
+ ),
+ 'gipfl/linux-health' => array(
+ 'pretty_version' => 'v0.2.0',
+ 'version' => '0.2.0.0',
+ 'type' => 'library',
+ 'install_path' => __DIR__ . '/../gipfl/linux-health',
+ 'aliases' => array(),
+ 'reference' => '4d212535565f353f88c9eef3ecb54e20c40b8b37',
+ 'dev_requirement' => false,
+ ),
+ 'gipfl/log' => array(
+ 'pretty_version' => 'v0.7.0',
+ 'version' => '0.7.0.0',
+ 'type' => 'library',
+ 'install_path' => __DIR__ . '/../gipfl/log',
+ 'aliases' => array(),
+ 'reference' => 'df770bdc9c09b0428831345af7ce7d45a0c3a1bb',
+ 'dev_requirement' => false,
+ ),
+ 'gipfl/openrpc' => array(
+ 'pretty_version' => 'v0.2.1',
+ 'version' => '0.2.1.0',
+ 'type' => 'library',
+ 'install_path' => __DIR__ . '/../gipfl/openrpc',
+ 'aliases' => array(),
+ 'reference' => 'a29b8da2cdaf7d757d86aa0fe6d719cf68fb95ad',
+ 'dev_requirement' => false,
+ ),
+ 'gipfl/process' => array(
+ 'pretty_version' => 'v0.4.0',
+ 'version' => '0.4.0.0',
+ 'type' => 'library',
+ 'install_path' => __DIR__ . '/../gipfl/process',
+ 'aliases' => array(),
+ 'reference' => 'ddbc3971cf56b35ce17d7cc8aaf241e87a792938',
+ 'dev_requirement' => false,
+ ),
+ 'gipfl/protocol' => array(
+ 'pretty_version' => 'v0.2.0',
+ 'version' => '0.2.0.0',
+ 'type' => 'library',
+ 'install_path' => __DIR__ . '/../gipfl/protocol',
+ 'aliases' => array(),
+ 'reference' => 'bf1a0e160fe6f74c6be60be569c466d57551e73d',
+ 'dev_requirement' => false,
+ ),
+ 'gipfl/protocol-jsonrpc' => array(
+ 'pretty_version' => 'v0.11.0',
+ 'version' => '0.11.0.0',
+ 'type' => 'library',
+ 'install_path' => __DIR__ . '/../gipfl/protocol-jsonrpc',
+ 'aliases' => array(),
+ 'reference' => '30b0241c67019746fce0b464543d8f936bdb9b68',
+ 'dev_requirement' => false,
+ ),
+ 'gipfl/protocol-netstring' => array(
+ 'pretty_version' => 'v0.1.1',
+ 'version' => '0.1.1.0',
+ 'type' => 'library',
+ 'install_path' => __DIR__ . '/../gipfl/protocol-netstring',
+ 'aliases' => array(),
+ 'reference' => 'c0021ea9d69883760ac176121041a05c9a51ca11',
+ 'dev_requirement' => false,
+ ),
+ 'gipfl/react-utils' => array(
+ 'pretty_version' => 'v0.3.0',
+ 'version' => '0.3.0.0',
+ 'type' => 'library',
+ 'install_path' => __DIR__ . '/../gipfl/react-utils',
+ 'aliases' => array(),
+ 'reference' => '37ff2daa4bcc36aced4cc9df5198caad519d7bdb',
+ 'dev_requirement' => false,
+ ),
+ 'gipfl/simple-daemon' => array(
+ 'pretty_version' => 'v0.6.0',
+ 'version' => '0.6.0.0',
+ 'type' => 'library',
+ 'install_path' => __DIR__ . '/../gipfl/simple-daemon',
+ 'aliases' => array(),
+ 'reference' => 'b7cd3be54c171d20c6692dbb331a3ae6fe90f276',
+ 'dev_requirement' => false,
+ ),
+ 'gipfl/socket' => array(
+ 'pretty_version' => 'v0.4.0',
+ 'version' => '0.4.0.0',
+ 'type' => 'library',
+ 'install_path' => __DIR__ . '/../gipfl/socket',
+ 'aliases' => array(),
+ 'reference' => '1a2c78709e0bcf7de2469860fbfa807a4bcee555',
+ 'dev_requirement' => false,
+ ),
+ 'gipfl/stream' => array(
+ 'pretty_version' => 'v0.2.0',
+ 'version' => '0.2.0.0',
+ 'type' => 'library',
+ 'install_path' => __DIR__ . '/../gipfl/stream',
+ 'aliases' => array(),
+ 'reference' => 'ae0f8f4fd47a6dcc57885b232e239b0ba82889a3',
+ 'dev_requirement' => false,
+ ),
+ 'gipfl/systemd' => array(
+ 'pretty_version' => 'v0.3.0',
+ 'version' => '0.3.0.0',
+ 'type' => 'library',
+ 'install_path' => __DIR__ . '/../gipfl/systemd',
+ 'aliases' => array(),
+ 'reference' => 'ccfabb352a9ea9fd33d4cb426bc3793a651bf466',
+ 'dev_requirement' => false,
+ ),
+ 'gipfl/translation' => array(
+ 'pretty_version' => 'v0.1.1',
+ 'version' => '0.1.1.0',
+ 'type' => 'library',
+ 'install_path' => __DIR__ . '/../gipfl/translation',
+ 'aliases' => array(),
+ 'reference' => 'e974e6a2f0b524678b6554ee0046b5dd5f70a25d',
+ 'dev_requirement' => false,
+ ),
+ 'gipfl/web' => array(
+ 'pretty_version' => 'v0.11.0',
+ 'version' => '0.11.0.0',
+ 'type' => 'library',
+ 'install_path' => __DIR__ . '/../gipfl/web',
+ 'aliases' => array(),
+ 'reference' => 'd331fe195f7bab08561ee015ba56edeb334a1533',
+ 'dev_requirement' => false,
+ ),
+ 'gipfl/zfdb' => array(
+ 'pretty_version' => 'v0.4.0',
+ 'version' => '0.4.0.0',
+ 'type' => 'library',
+ 'install_path' => __DIR__ . '/../gipfl/zfdb',
+ 'aliases' => array(),
+ 'reference' => 'beb7ceabc80e8c81f0e237afcfb064a67e1432d6',
+ 'dev_requirement' => false,
+ ),
+ 'gipfl/zfdbstore' => array(
+ 'pretty_version' => 'v0.3.0',
+ 'version' => '0.3.0.0',
+ 'type' => 'library',
+ 'install_path' => __DIR__ . '/../gipfl/zfdbstore',
+ 'aliases' => array(),
+ 'reference' => '5296226865a0c053f29f57a1bccd50b17cab5248',
+ 'dev_requirement' => false,
+ ),
+ 'guzzlehttp/psr7' => array(
+ 'dev_requirement' => false,
+ 'replaced' => array(
+ 0 => '1.6.1',
+ ),
+ ),
+ 'icinga/incubator' => array(
+ 'pretty_version' => 'dev-master',
+ 'version' => 'dev-master',
+ 'type' => 'library',
+ 'install_path' => __DIR__ . '/../../',
+ 'aliases' => array(),
+ 'reference' => '03cc0aac078de25564d09c13ae7de6f0ce9c1b3b',
+ 'dev_requirement' => false,
+ ),
+ 'ipl/html' => array(
+ 'dev_requirement' => false,
+ 'replaced' => array(
+ 0 => 'v0.3.0',
+ ),
+ ),
+ 'ipl/orm' => array(
+ 'dev_requirement' => false,
+ 'replaced' => array(
+ 0 => 'v0.1.0',
+ ),
+ ),
+ 'ipl/sql' => array(
+ 'dev_requirement' => false,
+ 'replaced' => array(
+ 0 => 'v0.1.0',
+ ),
+ ),
+ 'ipl/stdlib' => array(
+ 'dev_requirement' => false,
+ 'replaced' => array(
+ 0 => 'v0.5.0',
+ ),
+ ),
+ 'ipl/validator' => array(
+ 'dev_requirement' => false,
+ 'replaced' => array(
+ 0 => 'v0.1.0',
+ ),
+ ),
+ 'ipl/web' => array(
+ 'dev_requirement' => false,
+ 'replaced' => array(
+ 0 => 'v0.1.0',
+ ),
+ ),
+ 'paragonie/random_compat' => array(
+ 'dev_requirement' => false,
+ 'replaced' => array(
+ 0 => 'v2.0.18',
+ ),
+ ),
+ 'predis/predis' => array(
+ 'dev_requirement' => false,
+ 'replaced' => array(
+ 0 => 'v1.1.6',
+ ),
+ ),
+ 'psr/http-message' => array(
+ 'dev_requirement' => false,
+ 'replaced' => array(
+ 0 => '1.0.1',
+ ),
+ ),
+ 'psr/log' => array(
+ 'pretty_version' => '1.1.4',
+ 'version' => '1.1.4.0',
+ 'type' => 'library',
+ 'install_path' => __DIR__ . '/../psr/log',
+ 'aliases' => array(),
+ 'reference' => 'd49695b909c3b7628b6289db5479a1c204601f11',
+ 'dev_requirement' => false,
+ ),
+ 'ralouphie/getallheaders' => array(
+ 'dev_requirement' => false,
+ 'replaced' => array(
+ 0 => '3.0.3',
+ ),
+ ),
+ 'ramsey/uuid' => array(
+ 'dev_requirement' => false,
+ 'replaced' => array(
+ 0 => '3.9.3',
+ ),
+ ),
+ 'react/cache' => array(
+ 'dev_requirement' => false,
+ 'replaced' => array(
+ 0 => 'v1.1.0',
+ ),
+ ),
+ 'react/child-process' => array(
+ 'dev_requirement' => false,
+ 'replaced' => array(
+ 0 => 'v0.6.1',
+ ),
+ ),
+ 'react/datagram' => array(
+ 'dev_requirement' => false,
+ 'replaced' => array(
+ 0 => 'v1.5.0',
+ ),
+ ),
+ 'react/dns' => array(
+ 'dev_requirement' => false,
+ 'replaced' => array(
+ 0 => 'v1.4.0',
+ ),
+ ),
+ 'react/event-loop' => array(
+ 'dev_requirement' => false,
+ 'replaced' => array(
+ 0 => 'v1.1.1',
+ ),
+ ),
+ 'react/http' => array(
+ 'dev_requirement' => false,
+ 'replaced' => array(
+ 0 => 'v1.1.0',
+ ),
+ ),
+ 'react/http-client' => array(
+ 'dev_requirement' => false,
+ 'replaced' => array(
+ 0 => 'v0.5.10',
+ ),
+ ),
+ 'react/promise' => array(
+ 'dev_requirement' => false,
+ 'replaced' => array(
+ 0 => 'v2.8.0',
+ ),
+ ),
+ 'react/promise-stream' => array(
+ 'dev_requirement' => false,
+ 'replaced' => array(
+ 0 => 'v1.2.0',
+ ),
+ ),
+ 'react/promise-timer' => array(
+ 'dev_requirement' => false,
+ 'replaced' => array(
+ 0 => 'v1.6.0',
+ ),
+ ),
+ 'react/socket' => array(
+ 'dev_requirement' => false,
+ 'replaced' => array(
+ 0 => 'v1.6.0',
+ ),
+ ),
+ 'react/stream' => array(
+ 'dev_requirement' => false,
+ 'replaced' => array(
+ 0 => 'v1.1.1',
+ ),
+ ),
+ 'ringcentral/psr7' => array(
+ 'dev_requirement' => false,
+ 'replaced' => array(
+ 0 => '1.3.0',
+ ),
+ ),
+ 'symfony/polyfill-ctype' => array(
+ 'dev_requirement' => false,
+ 'replaced' => array(
+ 0 => 'v1.18.1',
+ ),
+ ),
+ ),
+);
diff --git a/vendor/composer/platform_check.php b/vendor/composer/platform_check.php
new file mode 100644
index 0000000..862662c
--- /dev/null
+++ b/vendor/composer/platform_check.php
@@ -0,0 +1,26 @@
+<?php
+
+// platform_check.php @generated by Composer
+
+$issues = array();
+
+if (!(PHP_VERSION_ID >= 50603)) {
+ $issues[] = 'Your Composer dependencies require a PHP version ">= 5.6.3". You are running ' . PHP_VERSION . '.';
+}
+
+if ($issues) {
+ if (!headers_sent()) {
+ header('HTTP/1.1 500 Internal Server Error');
+ }
+ if (!ini_get('display_errors')) {
+ if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') {
+ fwrite(STDERR, 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . implode(PHP_EOL, $issues) . PHP_EOL.PHP_EOL);
+ } elseif (!headers_sent()) {
+ echo 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . str_replace('You are running '.PHP_VERSION.'.', '', implode(PHP_EOL, $issues)) . PHP_EOL.PHP_EOL;
+ }
+ }
+ trigger_error(
+ 'Composer detected issues in your platform: ' . implode(' ', $issues),
+ E_USER_ERROR
+ );
+}
diff --git a/vendor/gipfl/calendar/composer.json b/vendor/gipfl/calendar/composer.json
new file mode 100644
index 0000000..5e84ef6
--- /dev/null
+++ b/vendor/gipfl/calendar/composer.json
@@ -0,0 +1,32 @@
+{
+ "name": "gipfl/calendar",
+ "type": "library",
+ "description": "Calendar Utils",
+ "keywords": ["calendar"],
+ "homepage": "https://github.com/gipfl/calendar",
+ "authors": [
+ {
+ "name": "Thomas Gelf",
+ "email": "thomas@gelf.net"
+ }
+ ],
+ "config": {
+ "sort-packages": true
+ },
+ "require": {
+ "php": ">=5.4.0",
+ "gipfl/format": ">=0.3",
+ "gipfl/icingaweb2": ">=0.4.0",
+ "gipfl/translation": ">=0.1.1"
+ },
+ "autoload": {
+ "psr-4": {
+ "gipfl\\Calendar\\": "src"
+ }
+ },
+ "autoload-dev": {
+ "psr-4": {
+ "gipfl\\Tests\\Calendar\\": "tests"
+ }
+ }
+}
diff --git a/vendor/gipfl/calendar/src/Calendar.php b/vendor/gipfl/calendar/src/Calendar.php
new file mode 100644
index 0000000..e81227c
--- /dev/null
+++ b/vendor/gipfl/calendar/src/Calendar.php
@@ -0,0 +1,246 @@
+<?php
+
+namespace gipfl\Calendar;
+
+use gipfl\Format\LocalTimeFormat;
+use InvalidArgumentException;
+
+class Calendar
+{
+ const FIRST_IS_MONDAY = 1;
+ const FIRST_IS_SUNDAY = 0;
+
+ protected $firstOfWeek;
+
+ protected $weekDays = [];
+
+ protected $shortWeekDays = [];
+
+ protected $timeFormat;
+
+ public function __construct($firstOfWeek = self::FIRST_IS_MONDAY)
+ {
+ $this->timeFormat = new LocalTimeFormat();
+ $this->setFirstOfWeek($firstOfWeek);
+ }
+
+ public function firstOfWeekIsMonday()
+ {
+ return $this->firstOfWeek === self::FIRST_IS_MONDAY;
+ }
+
+ public function firstOfWeekIsSunday()
+ {
+ return $this->firstOfWeek === self::FIRST_IS_SUNDAY;
+ }
+
+ public function setFirstOfWeek($firstOfWeek)
+ {
+ if ($firstOfWeek === self::FIRST_IS_SUNDAY || $firstOfWeek === self::FIRST_IS_MONDAY) {
+ if ($firstOfWeek !== $this->firstOfWeek) {
+ $this->firstOfWeek = $firstOfWeek;
+ $this->prepareWeekDays();
+ }
+
+ return $this;
+ } else {
+ throw new InvalidArgumentException(
+ "First day of week has to be either 0 or 1, got '$firstOfWeek'"
+ );
+ }
+ }
+
+ protected function prepareWeekDays()
+ {
+ if ($this->firstOfWeekIsSunday()) {
+ $start = '2019-02-03';
+ } else {
+ $start = '2019-02-04';
+ }
+
+ for ($i = 0; $i < 7; $i++) {
+ $day = strtotime("$start +{$i}days");
+ $this->weekDays[] = $this->timeFormat->getWeekdayName($day);
+ $this->shortWeekDays[] = $this->timeFormat->getShortWeekdayName($day);
+ }
+ }
+
+ public function listWeekDayNames()
+ {
+ return $this->weekDays;
+ }
+
+ public function listShortWeekDayNames()
+ {
+ return $this->shortWeekDays;
+ }
+
+ /**
+ * Either 'N' or 'w', depending on the first day of week
+ *
+ * @return string
+ */
+ protected function getDowFormat()
+ {
+ if ($this->firstOfWeekIsMonday()) {
+ // N -> 1-7 (Mo-Su)
+ return 'N';
+ } else {
+ // w -> 0-6 (Su-Sa)
+ return 'w';
+ }
+ }
+
+ /**
+ * @param $time
+ * @return int
+ */
+ protected function getWeekDay($time)
+ {
+ return (int) date($this->getDowFormat(), $time);
+ }
+
+ /**
+ * @param int $now
+ * @return array
+ */
+ public function getDaysForWeek($now)
+ {
+ $formatDow = $this->getDowFormat();
+ $today = date('Y-m-d', $now);
+ $day = $this->getFirstDayOfWeek($today);
+ $weekday = (int) date($formatDow, strtotime($day));
+ $week = [$weekday => $day];
+ for ($i = 1; $i < 7; $i++) {
+ $day = date('Y-m-d', strtotime("$day +1day"));
+ $weekday = (int) date($formatDow, strtotime($day));
+ $week[$weekday] = $day;
+ }
+
+ return $week;
+ }
+
+ /**
+ * @param int $now
+ * @return array
+ */
+ public function getWorkingDaysForWeek($now)
+ {
+ $formatDow = $this->getDowFormat();
+ $today = date('Y-m-d', $now);
+ $day = $this->getFirstDayOfWeek($today, self::FIRST_IS_MONDAY);
+ $weekday = (int) date($formatDow, strtotime($day));
+ $week = [$weekday => $day];
+ for ($i = 1; $i < 5; $i++) {
+ $day = date('Y-m-d', strtotime("$day +1day"));
+ $weekday = (int) date($formatDow, strtotime($day));
+ $week[$weekday] = $day;
+ }
+
+ return $week;
+ }
+
+ /**
+ * @param string $day
+ * @param int $firstOfWeek
+ * @return string
+ */
+ public function getFirstDayOfWeek($day, $firstOfWeek = null)
+ {
+ if ($firstOfWeek === null) {
+ $firstOfWeek = $this->firstOfWeek;
+ }
+ $dow = $this->getWeekDay(strtotime($day));
+ if ($dow > $firstOfWeek) {
+ $sub = $dow - $firstOfWeek;
+ return date('Y-m-d', strtotime("$day -{$sub}day"));
+ } else {
+ return $day;
+ }
+ }
+
+ /**
+ * @param string $day
+ * @param int $firstOfWeek
+ * @return string
+ */
+ protected function getLastDayOfWeek($day, $firstOfWeek = null)
+ {
+ if ($firstOfWeek === null) {
+ $firstOfWeek = $this->firstOfWeek;
+ }
+ $dow = $this->getWeekDay(strtotime($day));
+ $lastOfWeek = $firstOfWeek + 6;
+ if ($dow < $lastOfWeek) {
+ $add = $lastOfWeek - $dow;
+ return static::expressionToDate(static::incDay($day, $add));
+ } else {
+ return $day;
+ }
+ }
+
+ public function getWeekOfTheYear($day)
+ {
+ $time = strtotime($day);
+ // 0 = Sunday
+ if ($this->firstOfWeekIsSunday() && $this->getWeekDay($time) === 0) {
+ if (substr($time, 4, 6) === '-12-31') {
+ return (int) date('W', strtotime(static::decDay($day)));
+ } else {
+ return (int) date('W', strtotime(static::incDay($day)));
+ }
+ } else {
+ return (int) date('W', $time);
+ }
+ }
+
+ /**
+ * @param int $now
+ * @return array
+ */
+ public function getWeeksForMonth($now)
+ {
+ $first = date('Y-m-01', $now);
+ $last = date('Y-m-d', strtotime("$first +1month -1day"));
+
+ $formatDow = $this->getDowFormat();
+ $end = $this->getLastDayOfWeek($last);
+ $day = $this->getFirstDayOfWeek($first);
+ $formerWeekOfTheYear = 0;
+ $weeks = [];
+ while ($day <= $end) {
+ $weekOfTheYear = $this->getWeekOfTheYear($day);
+ if ($weekOfTheYear !== $formerWeekOfTheYear) {
+ $weeks[$weekOfTheYear] = [];
+ $week = & $weeks[$weekOfTheYear];
+ }
+
+ $weekday = (int) date($formatDow, strtotime($day));
+ $week[$weekday] = $day;
+ $day = date('Y-m-d', strtotime("$day +1day"));
+ $formerWeekOfTheYear = $weekOfTheYear;
+ }
+
+ return $weeks;
+ }
+
+ protected static function expressionToDate($expression)
+ {
+ return date('Y-m-d', strtotime($expression));
+ }
+
+ /**
+ * @param string $day
+ * @param int $increment days to add
+ * @return string
+ */
+ protected static function incDay($day, $increment = 1)
+ {
+ return sprintf('%s +%dday', $day, $increment);
+ }
+
+ protected static function decDay($day, $decrement = 1)
+ {
+ return sprintf('%s -%dday', $day, $decrement);
+ }
+}
diff --git a/vendor/gipfl/calendar/src/Widget/CalendarMonth.php b/vendor/gipfl/calendar/src/Widget/CalendarMonth.php
new file mode 100644
index 0000000..2fe1c5b
--- /dev/null
+++ b/vendor/gipfl/calendar/src/Widget/CalendarMonth.php
@@ -0,0 +1,177 @@
+<?php
+
+namespace gipfl\Calendar\Widget;
+
+use gipfl\Calendar\Calendar;
+use gipfl\Format\LocalTimeFormat;
+use gipfl\IcingaWeb2\Link;
+use gipfl\IcingaWeb2\Url;
+use gipfl\Translation\TranslationHelper;
+use ipl\Html\BaseHtmlElement;
+use ipl\Html\Html;
+use ipl\Html\HtmlElement;
+
+/**
+ * WARNING: API will change
+ */
+class CalendarMonth extends BaseHtmlElement
+{
+ use TranslationHelper;
+
+ protected $tag = 'div';
+
+ protected $defaultAttributes = [
+ 'id' => 'calendar-wrap'
+ ];
+
+ /** @var Calendar */
+ protected $calendar;
+
+ /** @var int */
+ protected $now;
+
+ /** @var Url */
+ protected $url;
+
+ /** @var HtmlElement */
+ protected $days = [];
+
+ protected $timeFormatter;
+
+ public function __construct(Calendar $calendar, Url $url, $now)
+ {
+ $this->now = $now;
+ $this->url = $url;
+ $this->calendar = $calendar;
+ $this->timeFormatter = new LocalTimeFormat();
+ }
+
+ protected function dayRow()
+ {
+ return Html::tag('ul', ['class' => 'days']);
+ }
+
+ /**
+ * @param $day
+ * @return HtmlElement
+ */
+ protected function getDay($day)
+ {
+ $this->ensureAssembled();
+ return $this->days[$day];
+ }
+
+ protected function hasDay($day)
+ {
+ $this->ensureAssembled();
+
+ return isset($this->days[$day]);
+ }
+
+ protected function createDay($day)
+ {
+ $title = (int) substr($day, -2);
+
+ if ($title === 1) {
+ $title = sprintf(
+ '%d %s',
+ $title,
+ $this->timeFormatter->getShortMonthName(strtotime($day))
+ );
+ }
+ $li = Html::tag(
+ 'li',
+ ['class' => 'day'],
+ Html::tag('div', ['class' => 'date'], $title)
+ );
+
+ $this->days[$day] = $li;
+
+ return $li;
+ }
+
+ public function addEvent($time, $text)
+ {
+ $day = date('Y-m-d', $time);
+ if (! $this->hasDay($day)) {
+ return $this;
+ }
+ // $this->getDay($day)->add(Html::tag('div', ['class' => 'event'], [
+ $this->getDay($day)->add(Html::tag('a', ['class' => 'event', 'href' => '#'], [
+ Html::tag('div', [
+ 'class' => 'event-time',
+ 'title' => date('Y-m-d H:i:s')
+ ], date('H:i', $time)),
+ Html::tag('div', ['class' => 'event-desc'], $text)
+ ]));
+
+ return $this;
+ }
+
+ protected function getFormerMonth()
+ {
+ $first = date('Y-m-01', $this->now);
+
+ return date('Y-m-d', strtotime("$first -1month"));
+ }
+
+ protected function getNextMonth()
+ {
+ $first = date('Y-m-01', $this->now);
+
+ return date('Y-m-d', strtotime("$first +1month"));
+ }
+
+ protected function getNavigationLinks()
+ {
+ return Html::tag('div', ['class' => 'calendar-navigation'], [
+ Link::create('<', $this->url->with('day', $this->getFormerMonth())),
+ Link::create('>', $this->url->with('day', $this->getNextMonth())),
+ ]);
+ }
+
+ protected function assemble()
+ {
+ $now = $this->now;
+ $today = date('Y-m-d', $now);
+
+ $this->add(
+ Html::tag('header', [
+ $this->getNavigationLinks(),
+ Html::tag('h1', date('F Y', $now))
+ ])
+ );
+
+ $calendar = Html::tag('div', ['class' => 'calendar']);
+ $calendar->add($this->weekdaysHeader());
+ $thisMonth = substr($today, 0, 7);
+
+ foreach ($this->calendar->getWeeksForMonth($now) as $cw => $week) {
+ $weekRow = $this->dayRow();
+ $weekRow->add(
+ Html::tag('li', [
+ 'class' => 'weekName'
+ ], Html::tag('span', sprintf($this->translate('Week %s'), $cw)))
+ );
+ foreach ($week as $day) {
+ $weekRow->add($this->createDay($day));
+ if (substr($day, 0, 7) !== $thisMonth) {
+ $this->getDay($day)->addAttributes(['class' => 'other-month']);
+ }
+ }
+ $calendar->add($weekRow);
+ }
+
+ $this->add($calendar);
+ }
+
+ protected function weekdaysHeader()
+ {
+ $ul = Html::tag('ul', ['class' => 'weekdays']);
+ foreach ($this->calendar->listWeekDayNames() as $weekday) {
+ $ul->add(Html::tag('li', $this->translate($weekday)));
+ }
+
+ return $ul;
+ }
+}
diff --git a/vendor/gipfl/calendar/src/Widget/CalendarMonthSummary.php b/vendor/gipfl/calendar/src/Widget/CalendarMonthSummary.php
new file mode 100644
index 0000000..950530f
--- /dev/null
+++ b/vendor/gipfl/calendar/src/Widget/CalendarMonthSummary.php
@@ -0,0 +1,278 @@
+<?php
+
+namespace gipfl\Calendar\Widget;
+
+use gipfl\Calendar\Calendar;
+use gipfl\Format\LocalTimeFormat;
+use gipfl\IcingaWeb2\Link;
+use gipfl\IcingaWeb2\Url;
+use gipfl\Translation\TranslationHelper;
+use ipl\Html\HtmlElement;
+use ipl\Html\Table;
+
+class CalendarMonthSummary extends Table
+{
+ use TranslationHelper;
+
+ protected $defaultAttributes = [
+ 'data-base-target' => '_next',
+ 'class' => 'calendar',
+ ];
+
+ protected $today;
+
+ protected $year;
+
+ protected $month;
+
+ protected $strMonth;
+
+ protected $strToday;
+
+ protected $days = [];
+
+ /** @var Calendar|null */
+ protected $calendar;
+
+ protected $showWeekNumbers = true;
+
+ protected $showOtherMonth = false;
+
+ protected $showGrayFuture = true;
+
+ protected $title;
+
+ protected $color = '255, 128, 0';
+
+ protected $forcedMax;
+
+ protected $timeFormat;
+
+ public function __construct($year, $month)
+ {
+ $this->year = $year;
+ $this->month = $month;
+ $this->strMonth = sprintf('%d-%02d', $year, $month);
+ $this->strToday = date('Y-m-d');
+ $this->timeFormat = new LocalTimeFormat();
+ }
+
+ public function setBaseColorRgb($red, $green, $blue)
+ {
+ $this->color = sprintf('%d, %d, %d', $red, $green, $blue);
+
+ return $this;
+ }
+
+ public function setCalendar(Calendar $calendar)
+ {
+ $this->calendar = $calendar;
+
+ return $this;
+ }
+
+ public function getCalendar()
+ {
+ if ($this->calendar === null) {
+ $this->calendar = new Calendar();
+ }
+
+ return $this->calendar;
+ }
+
+ public function addEvents($events, Url $baseUrl)
+ {
+ if (empty($events)) {
+ return $this;
+ }
+
+ if ($this->forcedMax === null) {
+ $max = max($events);
+ } else {
+ $max = $this->forcedMax;
+ }
+
+ if ($max === 0 || $max === null) {
+ return $this;
+ }
+
+ foreach ($events as $day => $count) {
+ if (! $this->hasDay($day)) {
+ continue;
+ }
+
+ if (! $this->showOtherMonth && $this->dayIsInThisMonth($day)) {
+ continue;
+ }
+
+ $text = (int) substr($day, -2);
+
+ $link = Link::create($text, $baseUrl->with('day', $day));
+ $alpha = $count / $max;
+
+ if ($alpha > 0.4) {
+ $link->addAttributes(['style' => 'color: white;']);
+ }
+ $link->addAttributes([
+ 'title' => sprintf('%d events', $count),
+ 'style' => sprintf(
+ 'background-color: rgba(%s, %.2F);',
+ $this->color,
+ $alpha
+ )
+ ]);
+
+ $this->getDay($day)->setContent($link);
+ }
+
+ return $this;
+ }
+
+ public function markNow($now = null)
+ {
+ if ($now === null) {
+ $now = time();
+ }
+ $this->today = date('Y-m-d', $now);
+
+ return $this;
+ }
+
+ public function setTitle($title)
+ {
+ $this->title = $title;
+
+ return $this;
+ }
+
+ protected function getTitle()
+ {
+ if ($this->title === null) {
+ $this->title = $this->getMonthName() . ' ' . $this->year;
+ }
+
+ return $this->title;
+ }
+
+ public function forceMax($max)
+ {
+ $this->forcedMax = $max;
+
+ return $this;
+ }
+
+ protected function getMonthAsTimestamp()
+ {
+ return strtotime($this->strMonth . '-01');
+ }
+
+ protected function assemble()
+ {
+ $this->setCaption($this->getTitle());
+ $this->getHeader()->add($this->createWeekdayHeader());
+ $calendar = $this->getCalendar();
+ foreach ($calendar->getWeeksForMonth($this->getMonthAsTimestamp()) as $cw => $week) {
+ $weekRow = $this->weekRow($cw);
+ foreach ($week as $wDay => $day) {
+ $dayElement = $this->createDay($day);
+ $otherMonth = $this->dayIsInThisMonth($day);
+ if ($wDay < 1 || $wDay > 5) {
+ $dayElement->addAttributes(['class' => 'weekend']);
+ }
+ $weekRow->add($dayElement);
+ }
+ $this->add($weekRow);
+ }
+ }
+
+ /**
+ * @param $day
+ * @return HtmlElement
+ */
+ protected function getDay($day)
+ {
+ $this->ensureAssembled();
+
+ return $this->days[$day];
+ }
+
+ protected function hasDay($day)
+ {
+ $this->ensureAssembled();
+
+ return isset($this->days[$day]);
+ }
+
+ protected function dayIsInThisMonth($day)
+ {
+ return substr($day, 0, 7) !== $this->strMonth;
+ }
+
+ protected function createDay($day)
+ {
+ $otherMonth = $this->dayIsInThisMonth($day);
+ $title = (int) substr($day, -2);
+ if ($otherMonth && ! $this->showOtherMonth) {
+ $title = '';
+ }
+ $td = Table::td($title);
+ $this->days[$day] = $td;
+
+ if ($otherMonth) {
+ $td->addAttributes(['class' => 'other-month']);
+ } elseif ($this->showGrayFuture && $day > $this->strToday) {
+ $td->addAttributes(['class' => 'future-day']);
+ }
+
+ // TODO: today VS strToday?!
+ if ($day === $this->today) {
+ $td->addAttributes(['class' => 'today']);
+ }
+
+ return $td;
+ }
+
+ protected function weekRow($cw)
+ {
+ $row = Table::tr();
+
+ if ($this->showWeekNumbers) {
+ $row->add(Table::th(sprintf('%02d', $cw), [
+ 'title' => sprintf($this->translate('Calendar Week %d'), $cw)
+ ]));
+ }
+
+ return $row;
+ }
+
+ protected function getMonthName()
+ {
+ return $this->timeFormat->getMonthName($this->getMonthAsTimestamp());
+ }
+
+ protected function createWeekdayHeader()
+ {
+ $calendar = $this->getCalendar();
+ $cols = $calendar->listShortWeekDayNames();
+ $row = Table::tr();
+ if ($this->showWeekNumbers) {
+ $row->add(Table::th(''));
+ }
+ if ($calendar->firstOfWeekIsMonday()) {
+ $weekend = [6 => true, 7 => true];
+ } else {
+ $weekend = [1 => true, 7 => true];
+ }
+ $wDay = 0;
+ foreach ($cols as $day) {
+ $wDay++;
+ $col = Table::th($day);
+ if (isset($weekend[$wDay])) {
+ $col->addAttributes(['class' => 'weekend']);
+ }
+ $row->add($col);
+ }
+
+ return $row;
+ }
+}
diff --git a/vendor/gipfl/cli/composer.json b/vendor/gipfl/cli/composer.json
new file mode 100644
index 0000000..370beed
--- /dev/null
+++ b/vendor/gipfl/cli/composer.json
@@ -0,0 +1,31 @@
+{
+ "name": "gipfl/cli",
+ "description": "CLI utilities",
+ "type": "library",
+ "license": "MIT",
+ "authors": [
+ {
+ "name": "Thomas Gelf",
+ "email": "thomas@gelf.net"
+ }
+ ],
+ "config": {
+ "sort-packages": true
+ },
+ "autoload": {
+ "psr-4": {
+ "gipfl\\Cli\\": "src"
+ }
+ },
+ "require": {
+ "php": ">=5.6.0",
+ "ext-mbstring": "*",
+ "ext-pcntl": "*",
+ "ext-posix": "*",
+ "react/stream": ">=1.1",
+ "react/promise": "^2"
+ },
+ "require-dev": {
+ "react/child-process": ">=0.6"
+ }
+}
diff --git a/vendor/gipfl/cli/src/AnsiScreen.php b/vendor/gipfl/cli/src/AnsiScreen.php
new file mode 100644
index 0000000..2ae3f40
--- /dev/null
+++ b/vendor/gipfl/cli/src/AnsiScreen.php
@@ -0,0 +1,128 @@
+<?php
+
+namespace gipfl\Cli;
+
+use InvalidArgumentException;
+
+/**
+ * Screen implementation for screens with ANSI escape code support
+ *
+ * @see http://en.wikipedia.org/wiki/ANSI_escape_code
+ */
+class AnsiScreen extends Screen
+{
+ const FG_COLORS = [
+ 'black' => '30',
+ 'darkgray' => '1;30',
+ 'red' => '31',
+ 'lightred' => '1;31',
+ 'green' => '32',
+ 'lightgreen' => '1;32',
+ 'brown' => '33',
+ 'yellow' => '1;33',
+ 'blue' => '34',
+ 'lightblue' => '1;34',
+ 'purple' => '35',
+ 'lightpurple' => '1;35',
+ 'cyan' => '36',
+ 'lightcyan' => '1;36',
+ 'lightgray' => '37',
+ 'white' => '1;37',
+ ];
+
+ const BG_COLORS = [
+ 'black' => '40',
+ 'red' => '41',
+ 'green' => '42',
+ 'brown' => '43',
+ 'blue' => '44',
+ 'purple' => '45',
+ 'cyan' => '46',
+ 'lightgray' => '47',
+ ];
+
+ /**
+ * Remove all ANSI escape codes from a given string
+ * @param $string
+ * @return string|string[]|null
+ */
+ public function stripAnsiCodes($string)
+ {
+ return \preg_replace('/\e\[?.*?[@-~]/', '', $string);
+ }
+
+ public function clear()
+ {
+ return "\033[2J" // Clear the whole screen
+ . "\033[1;1H" // Move the cursor to row 1, column 1
+ . "\033[1S"; // Scroll whole page up by 1 line (why?)
+ }
+
+ public function colorize($text, $fgColor = null, $bgColor = null)
+ {
+ return $this->startColor($fgColor, $bgColor)
+ . $text
+ . "\033[0m"; // Reset color codes
+ }
+
+ public function strlen($string)
+ {
+ return parent::strlen($this->stripAnsiCodes($string));
+ }
+
+ public function underline($text)
+ {
+ return "\033[4m"
+ . $text
+ . "\033[0m"; // Reset color codes
+ }
+
+ protected function fgColor($color)
+ {
+ if (! \array_key_exists($color, static::FG_COLORS)) {
+ throw new InvalidArgumentException(
+ "There is no such foreground color: $color"
+ );
+ }
+
+ return static::FG_COLORS[$color];
+ }
+
+ protected function bgColor($color)
+ {
+ if (! \array_key_exists($color, static::BG_COLORS)) {
+ throw new InvalidArgumentException(
+ "There is no such background color: $color"
+ );
+ }
+
+ return static::BG_COLORS[$color];
+ }
+
+ protected function startColor($fgColor = null, $bgColor = null)
+ {
+ $parts = [];
+ if ($fgColor !== null
+ && $bgColor !== null
+ && ! \array_key_exists($bgColor, static::BG_COLORS)
+ && \array_key_exists($bgColor, static::FG_COLORS)
+ && \array_key_exists($fgColor, static::BG_COLORS)
+ ) {
+ $parts[] = '7'; // reverse video, negative image
+ $parts[] = $this->bgColor($fgColor);
+ $parts[] = $this->fgColor($bgColor);
+ } else {
+ if ($fgColor !== null) {
+ $parts[] = $this->fgColor($fgColor);
+ }
+ if ($bgColor !== null) {
+ $parts[] = $this->bgColor($bgColor);
+ }
+ }
+ if (empty($parts)) {
+ return '';
+ }
+
+ return "\033[" . \implode(';', $parts) . 'm';
+ }
+}
diff --git a/vendor/gipfl/cli/src/Process.php b/vendor/gipfl/cli/src/Process.php
new file mode 100644
index 0000000..45c67b5
--- /dev/null
+++ b/vendor/gipfl/cli/src/Process.php
@@ -0,0 +1,141 @@
+<?php
+
+namespace gipfl\Cli;
+
+class Process
+{
+ /** @var string|null */
+ protected static $initialCwd;
+
+ /**
+ * Set the command/process title for this process
+ *
+ * @param $title
+ */
+ public static function setTitle($title)
+ {
+ if (function_exists('cli_set_process_title')) {
+ \cli_set_process_title($title);
+ }
+ }
+
+ /**
+ * Replace this process with a new instance of itself by executing the
+ * very same binary with the very same parameters
+ */
+ public static function restart()
+ {
+ // _ is only available when executed via shell
+ $binary = static::getEnv('_');
+ $argv = $_SERVER['argv'];
+ if (\strlen($binary) === 0) {
+ // Problem: this doesn't work if we changed working directory and
+ // called the binary with a relative path. Something that doesn't
+ // happen when started as a daemon, and when started manually we
+ // should have $_ from our shell.
+ $binary = static::absoluteFilename(\array_shift($argv));
+ } else {
+ \array_shift($argv);
+ }
+ \pcntl_exec($binary, $argv, static::getEnv());
+ }
+
+ /**
+ * Get the given ENV variable, null if not available
+ *
+ * Returns an array with all ENV variables if no $key is given
+ *
+ * @param string|null $key
+ * @return array|string|null
+ */
+ public static function getEnv($key = null)
+ {
+ if ($key !== null) {
+ return \getenv($key);
+ }
+
+ if (PHP_VERSION_ID > 70100) {
+ return \getenv();
+ } else {
+ $env = $_SERVER;
+ unset($env['argv'], $env['argc']);
+
+ return $env;
+ }
+ }
+
+ /**
+ * Get the path to the executed binary when starting this command
+ *
+ * This fails if we changed working directory and called the binary with a
+ * relative path. Something that doesn't happen when started as a daemon.
+ * When started manually we should have $_ from our shell.
+ *
+ * To be always on the safe side please call Process::getInitialCwd() once
+ * after starting your process and before switching directory. That way we
+ * preserve our initial working directory.
+ *
+ * @return mixed|string
+ */
+ public static function getBinaryPath()
+ {
+ if (isset($_SERVER['_'])) {
+ return $_SERVER['_'];
+ } else {
+ global $argv;
+
+ return static::absoluteFilename($argv[0]);
+ }
+ }
+
+ /**
+ * The working directory as given by getcwd() the very first time we
+ * called this method
+ *
+ * @return string
+ */
+ public static function getInitialCwd()
+ {
+ if (self::$initialCwd === null) {
+ self::$initialCwd = \getcwd();
+ }
+
+ return self::$initialCwd;
+ }
+
+ /**
+ * Returns the absolute filename for the given file
+ *
+ * If relative, it's calculated in relation to the given working directory.
+ * The current working directory is being used if null is given.
+ *
+ * @param $filename
+ * @param null $cwd
+ * @return string
+ */
+ public static function absoluteFilename($filename, $cwd = null)
+ {
+ $filename = \str_replace(
+ DIRECTORY_SEPARATOR . DIRECTORY_SEPARATOR,
+ DIRECTORY_SEPARATOR,
+ $filename
+ );
+ if ($filename[0] === '.') {
+ $filename = ($cwd ?: \getcwd()) . DIRECTORY_SEPARATOR . $filename;
+ }
+ $parts = \explode(DIRECTORY_SEPARATOR, $filename);
+ $result = [];
+ foreach ($parts as $part) {
+ if ($part === '.') {
+ continue;
+ }
+ if ($part === '..') {
+ \array_pop($result);
+ continue;
+ }
+ $result[] = $part;
+ }
+
+ return \implode(DIRECTORY_SEPARATOR, $result);
+ }
+}
diff --git a/vendor/gipfl/cli/src/Screen.php b/vendor/gipfl/cli/src/Screen.php
new file mode 100644
index 0000000..cb05a3f
--- /dev/null
+++ b/vendor/gipfl/cli/src/Screen.php
@@ -0,0 +1,190 @@
+<?php
+
+namespace gipfl\Cli;
+
+/**
+ * Base class providing minimal CLI Screen functionality. While classes
+ * extending this one (read: AnsiScreen) should implement all the fancy cool
+ * things, this base class makes sure that your code will still run in
+ * environments with no ANSI or similar support
+ *
+ * ```php
+ * $screen = Screen::instance();
+ * echo $screen->center($screen->underline('Hello world'));
+ * ```
+ */
+class Screen
+{
+ protected $isUtf8;
+
+ /**
+ * Get a new Screen instance.
+ *
+ * For now this is limited to either a very basic Screen implementation as
+ * a fall-back or an AnsiScreen implementation with more functionality
+ *
+ * @return AnsiScreen|Screen
+ */
+ public static function factory()
+ {
+ if (! defined('STDOUT')) {
+ return new Screen();
+ }
+ if (\function_exists('posix_isatty') && \posix_isatty(STDOUT)) {
+ return new AnsiScreen();
+ } else {
+ return new Screen();
+ }
+ }
+
+ /**
+ * Center the given string horizontally on the current screen
+ *
+ * @param $string
+ * @return string
+ */
+ public function center($string)
+ {
+ $len = $this->strlen($string);
+ $width = (int) \floor(($this->getColumns() + $len) / 2) - $len;
+
+ return \str_repeat(' ', $width) . $string;
+ }
+
+ /**
+ * Clear the screen
+ *
+ * Impossible for non-ANSI screens, so let's output a newline for now
+ *
+ * @return string
+ */
+ public function clear()
+ {
+ return "\n";
+ }
+
+ /**
+ * Colorize the given text. Has no effect on a basic Screen, all colors
+ * will be accepted. It's prefectly legal to provide background or foreground
+ * only
+ *
+ * Returns the very same string, eventually enriched with related ANSI codes
+ *
+ * @param $text
+ * @param null $fgColor
+ * @param null $bgColor
+ *
+ * @return mixed
+ */
+ public function colorize($text, $fgColor = null, $bgColor = null)
+ {
+ return $text;
+ }
+
+ /**
+ * Generate $count newline characters
+ *
+ * @param int $count
+ * @return string
+ */
+ public function newlines($count = 1)
+ {
+ return \str_repeat(PHP_EOL, $count);
+ }
+
+ /**
+ * Calculate the visible length of a given string. While this is simple on
+ * a non-ANSI-screen, such implementation will be required to strip control
+ * characters to get the correct result
+ *
+ * @param $string
+ * @return int
+ */
+ public function strlen($string)
+ {
+ if ($this->isUtf8()) {
+ return \mb_strlen($string, 'UTF-8');
+ } else {
+ return \strlen($string);
+ }
+ }
+
+ /**
+ * Underline the given text - if possible
+ *
+ * @return string
+ */
+ public function underline($text)
+ {
+ return $text;
+ }
+
+ /**
+ * Get the number of currently available columns. Please note that this
+ * might chance at any time while your program is running
+ *
+ * @return int
+ */
+ public function getColumns()
+ {
+ $cols = (int) \getenv('COLUMNS');
+ if (! $cols) {
+ // stty -a ?
+ $cols = (int) \exec('tput cols');
+ }
+ if (! $cols) {
+ $cols = 80;
+ }
+
+ return $cols;
+ }
+
+ /**
+ * Get the number of currently available rows. Please note that this
+ * might chance at any time while your program is running
+ *
+ * @return int
+ */
+ public function getRows()
+ {
+ $rows = (int) \getenv('ROWS');
+ if (! $rows) {
+ // stty -a ?
+ $rows = (int) \exec('tput lines');
+ }
+ if (! $rows) {
+ $rows = 25;
+ }
+
+ return $rows;
+ }
+
+ /**
+ * Whether we're on a UTF-8 screen. We assume latin1 otherwise, there is no
+ * support for additional encodings
+ *
+ * @return bool
+ */
+ public function isUtf8()
+ {
+ if ($this->isUtf8 === null) {
+ // null should equal 0 here, however seems to equal '' on some systems:
+ $current = \setlocale(LC_ALL, 0);
+
+ $parts = explode(';', $current);
+ $lc_parts = [];
+ foreach ($parts as $part) {
+ if (\strpos($part, '=') === false) {
+ continue;
+ }
+ list($key, $val) = explode('=', $part, 2);
+ $lc_parts[$key] = $val;
+ }
+
+ $this->isUtf8 = \array_key_exists('LC_CTYPE', $lc_parts)
+ && \preg_match('~\.UTF-8$~i', $lc_parts['LC_CTYPE']);
+ }
+
+ return $this->isUtf8;
+ }
+}
diff --git a/vendor/gipfl/cli/src/Spinner.php b/vendor/gipfl/cli/src/Spinner.php
new file mode 100644
index 0000000..b949526
--- /dev/null
+++ b/vendor/gipfl/cli/src/Spinner.php
@@ -0,0 +1,69 @@
+<?php
+
+namespace gipfl\Cli;
+
+use React\EventLoop\LoopInterface;
+use React\Promise\Deferred;
+use React\Promise\ExtendedPromiseInterface;
+
+class Spinner
+{
+ const ASCII_SLASH = ['/', '-', '\\', '|'];
+ const ASCII_BOUNCING_CIRCLE = ['.', 'o', 'O', '°', 'O', 'o'];
+ const ROTATING_HALF_CIRCLE = ['◑', '◒', '◐', '◓'];
+ const ROTATING_EARTH = ['🌎', '🌏', '🌍'];
+ const ROTATING_MOON = ['🌑', '🌒', '🌓', '🌔', '🌕', '🌖', '🌗', '🌘'];
+ const UP_DOWN_BAR = [' ', '_', '▁', '▃', '▄', '▅', '▆', '▇', '▆', '▅', '▄', '▃', '▁'];
+ const CLOCK = ['🕐', '🕑', '🕒', '🕓', '🕔', '🕕', '🕖', '🕗', '🕘', '🕙', '🕚', '🕛'];
+ const WAVING_DOTS = ['⢄', '⢂', '⢁', '⡁', '⡈', '⡐', '⡠', '⡐', '⡈', '⡁', '⢁', '⢂'];
+ const ROTATING_DOTS = ['⣷', '⣯', '⣟', '⡿', '⢿', '⣻', '⣽', '⣾'];
+
+ /** @var LoopInterface */
+ protected $loop;
+
+ protected $frames;
+
+ protected $frame = -1;
+
+ protected $count;
+
+ protected $delay;
+
+ public function __construct(LoopInterface $loop, array $frames = self::ASCII_SLASH)
+ {
+ $this->loop = $loop;
+ $this->frames = $frames;
+ $this->count = \count($frames);
+ $this->delay = ((int) (2 * 100 / $this->count)) / 100;
+ }
+
+ protected function getNextFrame()
+ {
+ $first = $this->frame === -1;
+ $this->frame++;
+ if ($this->frame >= $this->count) {
+ $this->frame = 0;
+ }
+
+ return $this->frames[$this->frame];
+ }
+
+ public function spinWhile(ExtendedPromiseInterface $promise, callable $renderer)
+ {
+ $next = function () use ($renderer) {
+ $renderer($this->getNextFrame());
+ };
+ $spinTimer = $this->loop->addPeriodicTimer($this->delay, $next);
+ $deferred = new Deferred(function () use ($spinTimer) {
+ $this->loop->cancelTimer($spinTimer);
+ });
+ $this->loop->futureTick($next);
+ $wait = $deferred->promise();
+ $cancel = function () use ($wait) {
+ $wait->cancel();
+ };
+ $promise->otherwise($cancel)->then($cancel);
+
+ return $promise;
+ }
+}
diff --git a/vendor/gipfl/cli/src/Tty.php b/vendor/gipfl/cli/src/Tty.php
new file mode 100644
index 0000000..efe5924
--- /dev/null
+++ b/vendor/gipfl/cli/src/Tty.php
@@ -0,0 +1,132 @@
+<?php
+
+namespace gipfl\Cli;
+
+use InvalidArgumentException;
+use React\EventLoop\LoopInterface;
+use React\Stream\ReadableResourceStream;
+use React\Stream\WritableResourceStream;
+use RuntimeException;
+use function defined;
+use function fstat;
+use function function_exists;
+use function is_bool;
+use function is_resource;
+use function is_string;
+use function posix_isatty;
+use function register_shutdown_function;
+use function stream_isatty;
+use function stream_set_blocking;
+use function strlen;
+use function var_export;
+
+class Tty
+{
+ protected $stdin;
+
+ protected $stdout;
+
+ protected $loop;
+
+ protected $echo = true;
+
+ /** @var TtyMode */
+ protected $ttyMode;
+
+ public function __construct(LoopInterface $loop)
+ {
+ $this->loop = $loop;
+ register_shutdown_function([$this, 'restore']);
+ $loop->futureTick(function () {
+ $this->initialize();
+ });
+ }
+
+ public function setEcho($echo)
+ {
+ if (! is_bool($echo) && ! is_string($echo) && strlen($echo) !== 1) {
+ throw new InvalidArgumentException(
+ "\$echo must be boolean or a single character, got " . var_export($echo, 1)
+ );
+ }
+ $this->echo = $echo;
+ if ($this->ttyMode) {
+ if ($echo) {
+ $this->ttyMode->enableFeature('echo');
+ } else {
+ $this->ttyMode->disableFeature('echo');
+ }
+ }
+
+ return $this;
+ }
+
+ public function stdin()
+ {
+ if ($this->stdin === null) {
+ $this->assertValidStdin();
+ $this->stdin = new ReadableResourceStream(STDIN, $this->loop);
+ }
+
+ return $this->stdin;
+ }
+
+ protected function hasStdin()
+ {
+ return defined('STDIN') && is_resource(STDIN) && fstat(STDIN) !== false;
+ }
+
+ protected function assertValidStdin()
+ {
+ if (! $this->hasStdin()) {
+ throw new RuntimeException('I have no STDIN');
+ }
+ }
+
+ public function stdout()
+ {
+ if ($this->stdout === null) {
+ $this->assertValidStdout();
+ $this->stdout = new WritableResourceStream(STDOUT, $this->loop);
+ }
+
+ return $this->stdout;
+ }
+
+ protected function hasStdout()
+ {
+ return defined('STDOUT') && is_resource(STDOUT) && fstat(STDOUT) !== false;
+ }
+
+ protected function assertValidStdout()
+ {
+ if (! $this->hasStdout()) {
+ throw new RuntimeException('I have no STDOUT');
+ }
+ }
+
+ protected function initialize()
+ {
+ $this->ttyMode = new TtyMode();
+ $this->ttyMode->setPreferredMode($this->echo);
+ }
+
+ public static function isSupported()
+ {
+ if (PHP_VERSION_ID >= 70200) {
+ return stream_isatty(STDIN);
+ } elseif (function_exists('posix_isatty')) {
+ return posix_isatty(STDIN);
+ } else {
+ return false;
+ }
+ }
+
+ public function restore()
+ {
+ if ($this->hasStdin()) {
+ // ReadableResourceStream sets blocking to false, let's restore this
+ stream_set_blocking(STDIN, true);
+ }
+ }
+}
diff --git a/vendor/gipfl/cli/src/TtyMode.php b/vendor/gipfl/cli/src/TtyMode.php
new file mode 100644
index 0000000..8b9c884
--- /dev/null
+++ b/vendor/gipfl/cli/src/TtyMode.php
@@ -0,0 +1,95 @@
+<?php
+
+namespace gipfl\Cli;
+
+use function escapeshellarg;
+use function register_shutdown_function;
+use function rtrim;
+use function shell_exec;
+
+class TtyMode
+{
+ protected $originalMode;
+
+ public function enableCanonicalMode()
+ {
+ $this->enableFeature('icanon');
+
+ return $this;
+ }
+
+ public function disableCanonicalMode()
+ {
+ $this->disableFeature('icanon');
+
+ return $this;
+ }
+
+ public function enableFeature(...$feature)
+ {
+ $this->preserve();
+ $cmd = 'stty ';
+ foreach ($feature as $f) {
+ $cmd .= escapeshellarg($f);
+ }
+
+ shell_exec($cmd);
+ }
+
+ public function disableFeature(...$feature)
+ {
+ $this->preserve();
+ $cmd = 'stty';
+ foreach ($feature as $f) {
+ $cmd .= ' -' . escapeshellarg($f);
+ }
+
+ shell_exec($cmd);
+ }
+
+ public function getCurrentMode()
+ {
+ return rtrim(shell_exec('stty -g'), PHP_EOL);
+ }
+
+ /**
+ * Helper allowing to call stty only once for the mose used flags, icanon and echo
+ * @param bool $echo
+ * @return $this
+ */
+ public function setPreferredMode($echo = true)
+ {
+ $this->preserve();
+ if ($echo) {
+ $this->disableFeature('icanon');
+ } else {
+ $this->disableFeature('icanon', 'echo');
+ }
+
+ return $this;
+ }
+
+ /**
+ * @internal
+ */
+ public function preserve($force = false)
+ {
+ if ($force || $this->originalMode === null) {
+ $this->originalMode = $this->getCurrentMode();
+ register_shutdown_function([$this, 'restore']);
+ }
+
+ return $this;
+ }
+
+ /**
+ * @internal
+ */
+ public function restore()
+ {
+ if ($this->originalMode) {
+ shell_exec('stty ' . escapeshellarg($this->originalMode));
+ $this->originalMode = null;
+ }
+ }
+}
diff --git a/vendor/gipfl/curl/LICENSE b/vendor/gipfl/curl/LICENSE
new file mode 100644
index 0000000..dd88e09
--- /dev/null
+++ b/vendor/gipfl/curl/LICENSE
@@ -0,0 +1,21 @@
+The MIT License
+
+Copyright (c) 2018 Thomas Gelf
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/vendor/gipfl/curl/composer.json b/vendor/gipfl/curl/composer.json
new file mode 100644
index 0000000..845793d
--- /dev/null
+++ b/vendor/gipfl/curl/composer.json
@@ -0,0 +1,29 @@
+{
+ "name": "gipfl/curl",
+ "description": "ReactPHP-friendly async CURL abstraction",
+ "type": "library",
+ "license": "MIT",
+ "authors": [
+ {
+ "name": "Thomas Gelf",
+ "email": "thomas@gelf.net"
+ }
+ ],
+ "config": {
+ "sort-packages": true
+ },
+ "autoload": {
+ "psr-4": {
+ "gipfl\\Curl\\": "src"
+ }
+ },
+ "require": {
+ "php": ">=5.6.3",
+ "ext-curl": "*",
+ "guzzlehttp/psr7": ">=1.6",
+ "psr/http-message": "^1.0",
+ "react/event-loop": ">=1.0",
+ "react/promise": ">=2",
+ "react/stream": ">=1.0"
+ }
+}
diff --git a/vendor/gipfl/curl/src/CurlAsync.php b/vendor/gipfl/curl/src/CurlAsync.php
new file mode 100644
index 0000000..f9bd5d0
--- /dev/null
+++ b/vendor/gipfl/curl/src/CurlAsync.php
@@ -0,0 +1,338 @@
+<?php
+
+namespace gipfl\Curl;
+
+use Exception;
+use GuzzleHttp\Psr7\Message;
+use GuzzleHttp\Psr7\Request;
+use Psr\Http\Message\RequestInterface;
+use React\EventLoop\LoopInterface;
+use React\EventLoop\TimerInterface;
+use React\Promise\Deferred;
+use RuntimeException;
+use Throwable;
+use function array_shift;
+use function count;
+use function curl_close;
+use function curl_error;
+use function curl_multi_add_handle;
+use function curl_multi_close;
+use function curl_multi_exec;
+use function curl_multi_getcontent;
+use function curl_multi_info_read;
+use function curl_multi_init;
+use function curl_multi_remove_handle;
+use function curl_multi_select;
+use function curl_multi_setopt;
+use function curl_multi_strerror;
+
+/**
+ * This class provides an async CURL abstraction layer fitting into a ReactPHP
+ * reality, implemented based on curl_multi.
+ *
+ * As long as there are requests pending, a timer fires
+ *
+ */
+class CurlAsync
+{
+ const DEFAULT_POLLING_INTERVAL = 0.03;
+
+ /** @var false|resource */
+ protected $handle;
+
+ /** @var Deferred[] resourceIdx => Deferred */
+ protected $running = [];
+
+ /** @var [ [0 => resourceIdx, 1 => Deferred], ... ] */
+ protected $pending = [];
+
+ /** @var array[] resourceIdx => options */
+ protected $pendingOptions = [];
+
+ /** @var RequestInterface[] resourceIdx => RequestInterface */
+ protected $pendingRequests = [];
+
+ /** @var array resourceIdx => resource */
+ protected $curl = [];
+
+ /** @var int */
+ protected $maxParallelRequests = 30;
+
+ /** @var LoopInterface */
+ protected $loop;
+
+ /** @var float */
+ protected $fastInterval = self::DEFAULT_POLLING_INTERVAL;
+
+ /** @var TimerInterface */
+ protected $fastTimer;
+
+ /**
+ * @param LoopInterface $loop
+ */
+ public function __construct(LoopInterface $loop)
+ {
+ $this->loop = $loop;
+ $this->handle = curl_multi_init();
+ // Hint: I had no specific reason to disable pipelining, nothing but
+ // the desire to ease debugging. So, in case you feel confident you might
+ // want to remove this line
+ curl_multi_setopt($this->handle, CURLMOPT_PIPELINING, 0);
+ if (! $this->handle) {
+ throw new RuntimeException('Failed to initialize curl_multi');
+ }
+ }
+
+ public function get($url, $headers = [], $curlOptions = [])
+ {
+ return $this->send(new Request('GET', $url, $headers), $curlOptions);
+ }
+
+ public function post($url, $headers = [], $body = null, $curlOptions = [])
+ {
+ return $this->send(new Request('POST', $url, $headers, $body), $curlOptions);
+ }
+
+ public function head($url, $headers = [])
+ {
+ return $this->send(new Request('HEAD', $url, $headers));
+ }
+
+ public function send(RequestInterface $request, $curlOptions = [])
+ {
+ $curl = CurlHandle::createForRequest($request, $curlOptions);
+ $idx = (int) $curl;
+ $this->curl[$idx] = $curl;
+ $this->pendingOptions[$idx] = $curlOptions;
+ $this->pendingRequests[$idx] = $request;
+ $deferred = new Deferred(function () use ($idx) {
+ $this->freeByResourceReference($idx);
+ });
+ $this->pending[] = [$idx, $deferred];
+ $this->loop->futureTick(function () {
+ $this->enablePolling();
+ $this->enqueueNextRequestIfAny();
+ });
+
+ return $deferred->promise();
+ }
+
+ /**
+ * @param int $max
+ * @return $this
+ */
+ public function setMaxParallelRequests($max)
+ {
+ $this->maxParallelRequests = (int) $max;
+
+ return $this;
+ }
+
+ public function getPendingCurlHandles()
+ {
+ return $this->curl;
+ }
+
+ protected function enqueueNextRequestIfAny()
+ {
+ while (count($this->pending) > 0 && count($this->running) < $this->maxParallelRequests) {
+ $next = array_shift($this->pending);
+ $resourceIdx = $next[0];
+ $this->running[$resourceIdx] = $next[1];
+ $curl = $this->curl[$resourceIdx];
+ // enqueued: curl_getinfo($curl, CURLINFO_EFFECTIVE_URL);
+ curl_multi_add_handle($this->handle, $curl);
+ }
+ }
+
+ public function rejectAllPendingRequests($reasonOrError = null)
+ {
+ $this->rejectAllRunningRequests($reasonOrError);
+ }
+
+ protected function rejectAllRunningRequests($reasonOrError = null)
+ {
+ $running = $this->running; // Hint: intentionally cloned
+ foreach ($running as $resourceNum => $deferred) {
+ $this->freeByResourceReference($resourceNum);
+ $deferred->reject($reasonOrError);
+ }
+ if (! empty($this->running)) {
+ throw new RuntimeException(
+ // Hint: should never be reached
+ 'All running requests should have been removed, but something has been left'
+ );
+ }
+ }
+
+ protected function rejectAllDeferredRequests($reasonOrError = null)
+ {
+ foreach ($this->pending as $pending) {
+ list($resourceNum, $deferred) = $pending;
+ $this->freeByResourceReference($resourceNum);
+ $deferred->reject($reasonOrError);
+ }
+
+ if (! empty($this->running)) {
+ throw new RuntimeException(
+ // Hint: should never be reached
+ 'All pending requests should have been removed, but something has been left'
+ );
+ }
+ }
+
+ /**
+ * Returns true in case at least one request completed
+ *
+ * @return bool
+ */
+ protected function checkForResults()
+ {
+ if (empty($this->running)) {
+ return false;
+ } else {
+ $handle = $this->handle;
+ do {
+ $status = curl_multi_exec($handle, $active);
+ } while ($status > 0);
+ // Hint: while ($status === CURLM_CALL_MULTI_PERFORM) ?
+
+ if ($status !== CURLM_OK) {
+ throw new RuntimeException(curl_multi_strerror($handle));
+ }
+ if ($active) {
+ $fds = curl_multi_select($handle, 0.01);
+ // We take no action here, we'll info_read anyways:
+ // $fds === -1 -> select failed, returning. Probably only happens when running out of FDs
+ // $fds === 0 -> Nothing to do
+ // TODO: figure how often we get here -> https://bugs.php.net/bug.php?id=61141
+ }
+ $gotResult = false;
+ while (false !== ($completed = curl_multi_info_read($handle))) {
+ $this->requestCompleted($handle, $completed);
+ if (empty($this->pending) && empty($this->running)) {
+ $this->disablePolling();
+ }
+ $gotResult = true;
+ }
+
+ return $gotResult;
+ }
+ }
+
+ protected function requestCompleted($handle, $completed)
+ {
+ $curl = $completed['handle'];
+ $resourceNum = (int) $curl; // Hint this is an object in PHP >= 8, a resource in older versions
+ $deferred = $this->running[$resourceNum];
+ $request = $this->pendingRequests[$resourceNum];
+ $options = $this->pendingOptions[$resourceNum];
+ $content = curl_multi_getcontent($curl);
+ curl_multi_remove_handle($handle, $curl);
+ $removeProxyHeaders = isset($options[CURLOPT_PROXYTYPE])
+ && $options[CURLOPT_PROXYTYPE] === CURLPROXY_HTTP
+ // We assume that CURLOPT_SUPPRESS_CONNECT_HEADERS has been set for the request
+ && !defined('CURLOPT_SUPPRESS_CONNECT_HEADERS');
+
+ if ($completed['result'] === CURLE_OK) {
+ $this->freeByResourceReference($resourceNum);
+ try {
+ $deferred->resolve($this->parseResponse($content, $removeProxyHeaders));
+ } catch (\Exception $e) {
+ $deferred->reject(new ResponseParseError($e->getMessage(), $request, null, $e->getCode(), $e));
+ }
+ } else {
+ try {
+ $deferred->resolve($this->parseResponse($content, $removeProxyHeaders));
+ } catch (\Exception $e) {
+ $response = null;
+ }
+ try {
+ $error = curl_error($curl);
+ if ($error === '') {
+ $error = 'Curl failed, but got no CURL error';
+ }
+ } catch (Throwable $e) {
+ $error = 'Unable to determine CURL error: ' . $e->getMessage();
+ } catch (Exception $e) {
+ $error = 'Unable to determine CURL error: ' . $e->getMessage();
+ }
+ $deferred->reject(new RequestError($error, $request, $response));
+ $this->freeByResourceReference($resourceNum);
+ }
+ }
+
+ protected function parseResponse($content, $stripProxyHeaders)
+ {
+ // This method can be removed once we support PHP 7.3+ only, as it
+ // has CURLOPT_SUPPRESS_CONNECT_HEADERS
+ $response = Message::parseResponse($content);
+ if ($stripProxyHeaders) {
+ $body = (string) $response->getBody();
+ if (preg_match('/^HTTP\/.*? [0-9]{3}[^\n]*\r?\n/s', $body)) {
+ // There is no such header on reused connections
+ $response = Message::parseResponse($body);
+ }
+ }
+
+ return $response;
+ }
+
+ /**
+ * Set the polling interval used while requests are pending. Defaults to
+ * self::DEFAULT_POLLING_INTERVAL
+ *
+ * @param float $interval
+ */
+ public function setInterval($interval)
+ {
+ if ($interval !== $this->fastInterval) {
+ $this->fastInterval = $interval;
+ $this->reEnableTimerIfActive();
+ }
+ }
+
+ protected function reEnableTimerIfActive()
+ {
+ if ($this->fastTimer !== null) {
+ $this->disablePolling();
+ $this->enablePolling();
+ }
+ }
+
+ /**
+ * Polling timer should be active only while requests are pending
+ */
+ protected function enablePolling()
+ {
+ if ($this->fastTimer === null) {
+ $this->fastTimer = $this->loop->addPeriodicTimer($this->fastInterval, function () {
+ if ($this->checkForResults()) {
+ $this->enqueueNextRequestIfAny();
+ }
+ });
+ }
+ }
+
+ protected function disablePolling()
+ {
+ if ($this->fastTimer) {
+ $this->loop->cancelTimer($this->fastTimer);
+ $this->fastTimer = null;
+ }
+ }
+
+ protected function freeByResourceReference($ref)
+ {
+ unset($this->pendingRequests[$ref]);
+ unset($this->pendingOptions[$ref]);
+ unset($this->running[$ref]);
+ curl_close($this->curl[$ref]);
+ unset($this->curl[$ref]);
+ }
+
+ public function __destruct()
+ {
+ curl_multi_close($this->handle);
+ }
+}
diff --git a/vendor/gipfl/curl/src/CurlHandle.php b/vendor/gipfl/curl/src/CurlHandle.php
new file mode 100644
index 0000000..d5309f0
--- /dev/null
+++ b/vendor/gipfl/curl/src/CurlHandle.php
@@ -0,0 +1,76 @@
+<?php
+
+namespace gipfl\Curl;
+
+use Psr\Http\Message\RequestInterface;
+
+class CurlHandle
+{
+ protected static $curlOptions = [
+ CURLOPT_HEADER => true,
+ CURLOPT_RETURNTRANSFER => true,
+ CURLOPT_CONNECTTIMEOUT => 5,
+ CURLOPT_SSL_VERIFYPEER => true,
+ CURLOPT_SSL_VERIFYHOST => 2,
+ CURLOPT_ENCODING => 'gzip',
+ CURLOPT_TCP_NODELAY => true,
+ CURLINFO_HEADER_OUT => true,
+ CURLOPT_TCP_KEEPALIVE => 1,
+ CURLOPT_BUFFERSIZE => 512 * 1024,
+ ];
+
+ public static function createForRequest(RequestInterface $request, $curlOptions = [])
+ {
+ $headers = [];
+ foreach ($request->getHeaders() as $name => $values) {
+ foreach ($values as $value) {
+ $headers[] = "$name: $value";
+ }
+ }
+ $body = $request->getBody();
+ if ($body->getSize() > 0) {
+ $body = $body->getContents();
+ } else {
+ $body = null;
+ }
+
+
+ $curl = curl_init();
+ $opts = static::prepareCurlOptions(
+ $request->getMethod(),
+ (string) $request->getUri(),
+ $body,
+ $headers,
+ $curlOptions
+ );
+ curl_setopt_array($curl, $opts);
+
+ return $curl;
+ }
+
+ protected static function prepareCurlOptions($method, $url, $body = null, $headers = [], $curlOptions = [])
+ {
+ $opts = $curlOptions + [
+ CURLOPT_CUSTOMREQUEST => $method,
+ CURLOPT_URL => $url,
+ ] + self::$curlOptions;
+
+ if (isset($opts[CURLOPT_HTTPHEADER])) {
+ $opts[CURLOPT_HTTPHEADER] = array_merge($opts[CURLOPT_HTTPHEADER], $headers);
+ } else {
+ $opts[CURLOPT_HTTPHEADER] = $headers;
+ }
+ if (isset($opts[CURLOPT_PROXYTYPE])
+ && $opts[CURLOPT_PROXYTYPE] === CURLPROXY_HTTP
+ && defined('CURLOPT_SUPPRESS_CONNECT_HEADERS')
+ ) {
+ $opts[CURLOPT_SUPPRESS_CONNECT_HEADERS] = true;
+ }
+
+ if ($body !== null) {
+ $opts[CURLOPT_POSTFIELDS] = $body;
+ }
+
+ return $opts;
+ }
+}
diff --git a/vendor/gipfl/curl/src/RequestError.php b/vendor/gipfl/curl/src/RequestError.php
new file mode 100644
index 0000000..a65df3a
--- /dev/null
+++ b/vendor/gipfl/curl/src/RequestError.php
@@ -0,0 +1,44 @@
+<?php
+
+namespace gipfl\Curl;
+
+use Exception;
+use Psr\Http\Message\RequestInterface;
+use Psr\Http\Message\ResponseInterface;
+
+class RequestError extends Exception // implements Psr\Http\Client\RequestExceptionInterface
+{
+ /** @var RequestInterface */
+ protected $request;
+
+ /** @var ResponseInterface */
+ protected $response;
+
+ public function __construct(
+ $message,
+ RequestInterface $request,
+ ?ResponseInterface $response = null,
+ $code = 0,
+ ?Exception $previous = null
+ ) {
+ parent::__construct($message, $code, $previous);
+ $this->request = $request;
+ $this->response = $response;
+ }
+
+ /**
+ * @return RequestInterface
+ */
+ public function getRequest()
+ {
+ return $this->request;
+ }
+
+ /**
+ * @return ?ResponseInterface
+ */
+ public function getResponse()
+ {
+ return $this->response;
+ }
+}
diff --git a/vendor/gipfl/curl/src/ResponseParseError.php b/vendor/gipfl/curl/src/ResponseParseError.php
new file mode 100644
index 0000000..5f127b6
--- /dev/null
+++ b/vendor/gipfl/curl/src/ResponseParseError.php
@@ -0,0 +1,7 @@
+<?php
+
+namespace gipfl\Curl;
+
+class ResponseParseError extends RequestError
+{
+}
diff --git a/vendor/gipfl/data-type/composer.json b/vendor/gipfl/data-type/composer.json
new file mode 100644
index 0000000..412c294
--- /dev/null
+++ b/vendor/gipfl/data-type/composer.json
@@ -0,0 +1,24 @@
+{
+ "name": "gipfl/data-type",
+ "description": "Serializable Data Types",
+ "type": "library",
+ "license": "MIT",
+ "authors": [
+ {
+ "name": "Thomas Gelf",
+ "email": "thomas@gelf.net"
+ }
+ ],
+ "config": {
+ "sort-packages": true
+ },
+ "autoload": {
+ "psr-4": {
+ "gipfl\\DataType\\": "src"
+ }
+ },
+ "require": {
+ "php": ">=5.6.0",
+ "gipfl/json": ">=0.2.0"
+ }
+}
diff --git a/vendor/gipfl/data-type/src/SetOfSettings.php b/vendor/gipfl/data-type/src/SetOfSettings.php
new file mode 100644
index 0000000..731a2d7
--- /dev/null
+++ b/vendor/gipfl/data-type/src/SetOfSettings.php
@@ -0,0 +1,79 @@
+<?php
+
+namespace gipfl\DataType;
+
+use gipfl\Json\JsonSerialization;
+use gipfl\Json\JsonString;
+use function ksort;
+
+class SetOfSettings implements JsonSerialization
+{
+ /** @var Settings[] */
+ protected $sections = [];
+
+ /**
+ * @param Settings[]|array|\stdClass $set
+ */
+ public function __construct(array $set = [])
+ {
+ foreach ((array) $set as $section => $settings) {
+ $this->setSection($section, $settings);
+ }
+ }
+
+ public static function fromSerialization($any)
+ {
+ return new static($any);
+ }
+
+ public function set($section, $setting, $value)
+ {
+ if (! isset($this->sections[$section])) {
+ $this->sections[$section] = new Settings();
+ }
+ $this->sections[$section]->set($setting, $value);
+
+ return $this;
+ }
+
+ public function get($section, $setting, $default = null)
+ {
+ if (isset($this->sections[$section])) {
+ return $this->sections[$section]->get($setting, $default);
+ }
+
+ return $default;
+ }
+
+ public function setSection($section, $settings)
+ {
+ if ($settings instanceof Settings) {
+ $this->sections[$section] = clone($settings);
+ } else {
+ $this->sections[$section] = new Settings($settings);
+ }
+
+ return $this;
+ }
+
+ public function cloneSection($section)
+ {
+ if (array_key_exists($section, $this->sections)) {
+ return clone($this->sections[$section]);
+ }
+
+ return new Settings();
+ }
+
+ public function equals(SetOfSettings $settings)
+ {
+ return JsonString::encode($settings) === JsonString::encode($this);
+ }
+
+ #[\ReturnTypeWillChange]
+ public function jsonSerialize()
+ {
+ ksort($this->sections);
+ return (object) $this->sections;
+ }
+}
diff --git a/vendor/gipfl/data-type/src/Settings.php b/vendor/gipfl/data-type/src/Settings.php
new file mode 100644
index 0000000..64b179d
--- /dev/null
+++ b/vendor/gipfl/data-type/src/Settings.php
@@ -0,0 +1,103 @@
+<?php
+
+namespace gipfl\DataType;
+
+use gipfl\Json\JsonSerialization;
+use gipfl\Json\JsonString;
+use gipfl\Json\SerializationHelper;
+use InvalidArgumentException;
+use stdClass;
+use function array_key_exists;
+use function ksort;
+
+class Settings implements JsonSerialization
+{
+ protected $settings = [];
+
+ /**
+ * @param object|array $settings
+ */
+ public function __construct($settings = [])
+ {
+ foreach ((array) $settings as $property => $value) {
+ $this->set($property, $value);
+ }
+ }
+
+ /**
+ * @param stdClass|array $object
+ * @return static
+ */
+ public static function fromSerialization($object)
+ {
+ return new static($object);
+ }
+
+ public function set($name, $value)
+ {
+ SerializationHelper::assertSerializableValue($value);
+ $this->settings[$name] = $value;
+ }
+
+ public function get($name, $default = null)
+ {
+ if ($this->has($name)) {
+ return $this->settings[$name];
+ }
+
+ return $default;
+ }
+
+ public function getArray($name, $default = [])
+ {
+ if ($this->has($name)) {
+ return (array) $this->settings[$name];
+ }
+
+ return $default;
+ }
+
+ public function requireArray($name)
+ {
+ return (array) $this->getRequired(($name));
+ }
+
+ public function getAsSettings($name, Settings $default = null)
+ {
+ if ($this->has($name)) {
+ return Settings::fromSerialization($this->settings[$name]);
+ }
+
+ if ($default === null) {
+ return new Settings();
+ }
+
+ return $default;
+ }
+
+ public function getRequired($name)
+ {
+ if ($this->has($name)) {
+ return $this->settings[$name];
+ }
+
+ throw new InvalidArgumentException("Setting '$name' is not available");
+ }
+
+ public function has($name)
+ {
+ return array_key_exists($name, $this->settings);
+ }
+
+ public function equals(Settings $settings)
+ {
+ return JsonString::encode($settings) === JsonString::encode($this);
+ }
+
+ #[\ReturnTypeWillChange]
+ public function jsonSerialize()
+ {
+ ksort($this->settings);
+ return (object) $this->settings;
+ }
+}
diff --git a/vendor/gipfl/db-migration/composer.json b/vendor/gipfl/db-migration/composer.json
new file mode 100644
index 0000000..da2ae4a
--- /dev/null
+++ b/vendor/gipfl/db-migration/composer.json
@@ -0,0 +1,16 @@
+{
+ "name": "gipfl/db-migration",
+ "description": "Simple DB migration helper",
+ "type": "library",
+ "require": {
+ "php": ">=5.6"
+ },
+ "config": {
+ "sort-packages": true
+ },
+ "autoload": {
+ "psr-4": {
+ "gipfl\\DbMigration\\": "src"
+ }
+ }
+}
diff --git a/vendor/gipfl/db-migration/src/Migration.php b/vendor/gipfl/db-migration/src/Migration.php
new file mode 100644
index 0000000..2e6c586
--- /dev/null
+++ b/vendor/gipfl/db-migration/src/Migration.php
@@ -0,0 +1,73 @@
+<?php
+
+namespace gipfl\DbMigration;
+
+use Exception;
+use gipfl\ZfDb\Adapter\Pdo\PdoAdapter as Db;
+use InvalidArgumentException;
+use RuntimeException;
+use Zend_Db_Adapter_Pdo_Abstract as ZfDb;
+
+class Migration
+{
+ /**
+ * @var string
+ */
+ protected $sql;
+
+ /**
+ * @var int
+ */
+ protected $version;
+
+ public function __construct($version, $sql)
+ {
+ $this->version = $version;
+ $this->sql = $sql;
+ }
+
+ /**
+ * @param Db|ZfDb $db
+ * @return $this
+ */
+ public function apply($db)
+ {
+ if (! ($db instanceof Db || $db instanceof ZfDb)) {
+ throw new InvalidArgumentException('$db must be an valid Zend_Db PDO adapter');
+ }
+ // TODO: this is fragile and depends on accordingly written schema files:
+ $sql = preg_replace('/-- .*$/m', '', $this->sql);
+ $queries = preg_split(
+ '/[\n\s\t]*;[\n\s\t]+/s',
+ $sql,
+ -1,
+ PREG_SPLIT_NO_EMPTY
+ );
+
+ if (empty($queries)) {
+ throw new RuntimeException(sprintf(
+ 'Migration %d has no queries',
+ $this->version
+ ));
+ }
+
+ try {
+ foreach ($queries as $query) {
+ if (preg_match('/^(?:OPTIMIZE|EXECUTE) /i', $query)) {
+ $db->query($query);
+ } else {
+ $db->exec($query);
+ }
+ }
+ } catch (Exception $e) {
+ throw new RuntimeException(sprintf(
+ 'Migration %d failed (%s) while running %s',
+ $this->version,
+ $e->getMessage(),
+ $query
+ ));
+ }
+
+ return $this;
+ }
+}
diff --git a/vendor/gipfl/db-migration/src/Migrations.php b/vendor/gipfl/db-migration/src/Migrations.php
new file mode 100644
index 0000000..2f85aa4
--- /dev/null
+++ b/vendor/gipfl/db-migration/src/Migrations.php
@@ -0,0 +1,299 @@
+<?php
+
+namespace gipfl\DbMigration;
+
+use DirectoryIterator;
+use Exception;
+use gipfl\ZfDb\Adapter\Exception\AdapterException;
+use gipfl\ZfDb\Adapter\Adapter as Db;
+use gipfl\ZfDb\Adapter\Pdo\Mysql;
+use gipfl\ZfDb\Adapter\Pdo\Pgsql;
+use InvalidArgumentException;
+use Zend_Db_Adapter_Pdo_Abstract as ZfDb;
+use Zend_Db_Adapter_Pdo_Mysql as ZfMysql;
+use Zend_Db_Adapter_Pdo_Pgsql as ZfPgsql;
+
+class Migrations
+{
+ const DB_TYPE_MYSQL = 'mysql';
+
+ const DB_TYPE_POSTGRESQL = 'pgsql';
+
+ /** @var Db */
+ protected $db;
+
+ /** @var string mysql or pgsql */
+ protected $dbType;
+
+ /** @var string */
+ protected $schemaDirectory;
+
+ /** @var string */
+ protected $tableName;
+
+ /**
+ * Migrations constructor.
+ *
+ * @param Db|ZfDb $db
+ * @param string $schemaDirectory
+ * @param string $tableName
+ */
+ public function __construct($db, $schemaDirectory, $tableName = 'schema_migration')
+ {
+ if (! ($db instanceof Db || $db instanceof ZfDb)) {
+ throw new InvalidArgumentException('$db must be an valid Zend_Db PDO adapter');
+ }
+ $this->db = $db;
+ if ($db instanceof Mysql || $db instanceof ZfMysql) {
+ $this->dbType = self::DB_TYPE_MYSQL;
+ } elseif ($db instanceof Pgsql || $db instanceof ZfPgsql) {
+ $this->dbType = self::DB_TYPE_POSTGRESQL;
+ } else {
+ throw new InvalidArgumentException(sprintf(
+ 'Migrations are currently supported for MySQL and PostgreSQL only, got %s',
+ get_class($db)
+ ));
+ }
+ $this->tableName = (string) $tableName;
+ $this->schemaDirectory = (string) $schemaDirectory;
+ }
+
+ /**
+ * Still unused
+ *
+ * @throws AdapterException|\Zend_Db_Adapter_Exception
+ */
+ protected function createMigrationsTable()
+ {
+ if ($this->dbType === self::DB_TYPE_POSTGRESQL) {
+ $create = /** @lang text */
+ <<<SQL
+
+CREATE TABLE {$this->tableName} (
+ schema_version SMALLINT NOT NULL,
+ migration_time TIMESTAMP WITH TIME ZONE NOT NULL,
+ PRIMARY KEY (schema_version)
+);
+
+SQL;
+ } else {
+ $create = /** @lang text */
+ <<<SQL
+CREATE TABLE {$this->tableName} (
+ schema_version SMALLINT UNSIGNED NOT NULL,
+ migration_time DATETIME NOT NULL,
+ PRIMARY KEY (schema_version)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE utf8mb4_bin;
+SQL;
+ }
+ $this->db->exec($create);
+ }
+
+ /**
+ * @return int
+ */
+ public function getLastMigrationNumber()
+ {
+ try {
+ $query = $this->db->select()->from(
+ ['m' => $this->getTableName()],
+ ['schema_version' => 'MAX(schema_version)']
+ );
+
+ return (int) $this->db->fetchOne($query);
+ } catch (Exception $e) {
+ return 0;
+ }
+ }
+
+ /**
+ * @return string
+ */
+ protected function getTableName()
+ {
+ return $this->tableName;
+ }
+
+ /**
+ * @return bool
+ */
+ public function hasAnyTable()
+ {
+ return count($this->db->listTables()) > 0;
+ }
+
+ /**
+ * @return bool
+ */
+ public function hasTable($tableName)
+ {
+ return in_array($tableName, $this->db->listTables());
+ }
+
+ /**
+ * @return bool
+ */
+ public function hasMigrationsTable()
+ {
+ return $this->hasTable($this->tableName);
+ }
+
+ /**
+ * @return bool
+ */
+ public function hasSchema()
+ {
+ return $this->listPendingMigrations() !== [0];
+ }
+
+ /**
+ * @return bool
+ */
+ public function hasPendingMigrations()
+ {
+ return $this->countPendingMigrations() > 0;
+ }
+
+ /**
+ * @return int
+ */
+ public function countPendingMigrations()
+ {
+ return count($this->listPendingMigrations());
+ }
+
+ /**
+ * @return Migration[]
+ */
+ public function getPendingMigrations()
+ {
+ $migrations = array();
+ foreach ($this->listPendingMigrations() as $version) {
+ $migrations[] = new Migration(
+ $version,
+ $this->loadMigrationFile($version)
+ );
+ }
+
+ return $migrations;
+ }
+
+ /**
+ * @return $this
+ */
+ public function applyPendingMigrations()
+ {
+ foreach ($this->getPendingMigrations() as $migration) {
+ $migration->apply($this->db);
+ }
+
+ return $this;
+ }
+
+ /**
+ * @return int[]
+ */
+ public function listPendingMigrations()
+ {
+ $lastMigration = $this->getLastMigrationNumber();
+ if ($lastMigration === 0) {
+ return [0];
+ }
+
+ return $this->listMigrationsAfter($this->getLastMigrationNumber());
+ }
+
+ /**
+ * @return int[]
+ */
+ public function listAllMigrations()
+ {
+ $dir = $this->getMigrationsDirectory();
+ $versions = [];
+
+ if (! is_readable($dir)) {
+ return $versions;
+ }
+
+ foreach (new DirectoryIterator($dir) as $file) {
+ if ($file->isDot()) {
+ continue;
+ }
+
+ $filename = $file->getFilename();
+ if (preg_match('/^upgrade_(\d+)\.sql$/', $filename, $match)) {
+ $versions[] = (int) $match[1];
+ }
+ }
+
+ sort($versions);
+
+ return $versions;
+ }
+
+ /**
+ * @param $version
+ * @return false|string
+ */
+ public function loadMigrationFile($version)
+ {
+ if ($version === 0) {
+ $filename = $this->getFullSchemaFile();
+ } else {
+ $filename = sprintf(
+ '%s/upgrade_%d.sql',
+ $this->getMigrationsDirectory(),
+ $version
+ );
+ }
+
+ return file_get_contents($filename);
+ }
+
+ /**
+ * @param $version
+ * @return int[]
+ */
+ protected function listMigrationsAfter($version)
+ {
+ $filtered = [];
+ foreach ($this->listAllMigrations() as $available) {
+ if ($available > $version) {
+ $filtered[] = $available;
+ }
+ }
+
+ return $filtered;
+ }
+
+ /**
+ * @param ?string $subDirectory
+ * @return string
+ */
+ public function getSchemaDirectory($subDirectory = null)
+ {
+ if ($subDirectory === null) {
+ return $this->schemaDirectory;
+ } else {
+ return $this->schemaDirectory . '/' . ltrim($subDirectory, '/');
+ }
+ }
+
+ /**
+ * @return string
+ */
+ public function getMigrationsDirectory()
+ {
+ return $this->getSchemaDirectory($this->dbType . '-migrations');
+ }
+
+ /**
+ * @return string
+ */
+ protected function getFullSchemaFile()
+ {
+ return $this->getSchemaDirectory(
+ $this->dbType. '.sql'
+ );
+ }
+}
diff --git a/vendor/gipfl/diff/LICENSE b/vendor/gipfl/diff/LICENSE
new file mode 100644
index 0000000..dd88e09
--- /dev/null
+++ b/vendor/gipfl/diff/LICENSE
@@ -0,0 +1,21 @@
+The MIT License
+
+Copyright (c) 2018 Thomas Gelf
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/vendor/gipfl/diff/composer.json b/vendor/gipfl/diff/composer.json
new file mode 100644
index 0000000..05c12a7
--- /dev/null
+++ b/vendor/gipfl/diff/composer.json
@@ -0,0 +1,25 @@
+{
+ "name": "gipfl/diff",
+ "description": "php-diff wrapper supporting ipl/html",
+ "type": "library",
+ "license": "MIT",
+ "authors": [
+ {
+ "name": "Thomas Gelf",
+ "email": "thomas@gelf.net"
+ }
+ ],
+ "config": {
+ "sort-packages": true
+ },
+ "autoload": {
+ "psr-4": {
+ "gipfl\\Diff\\": "src"
+ }
+ },
+ "require": {
+ "php": ">=5.6.0",
+ "ext-mbstring": "*",
+ "ipl/html": ">=0.2"
+ }
+}
diff --git a/vendor/gipfl/diff/public/css/diff.less b/vendor/gipfl/diff/public/css/diff.less
new file mode 100644
index 0000000..1dc2ef6
--- /dev/null
+++ b/vendor/gipfl/diff/public/css/diff.less
@@ -0,0 +1,133 @@
+@color-diff-ins: @color-ok;
+@color-diff-del: @color-critical;
+@color-diff-changed-old: fade(@color-critical, 30%);
+@color-diff-changed-new: fade(@color-ok, 30%);
+@color-diff-green-shade-light: #dfd;
+@color-diff-pale-green: #9e9;
+@color-diff-text-on-diff: #051030;
+@color-change-replace-del: #e99;
+
+.Differences {
+ width: 100%;
+ table-layout: fixed;
+ empty-cells: show;
+}
+
+.Differences thead {
+ display: none;
+}
+
+.Differences thead th {
+ text-align: left;
+ padding-left: 4 / 14 * 16em;
+}
+
+.Differences tbody th {
+ text-align: right;
+ width: 4em;
+ padding: 1px 2px;
+ border-right: 1px solid @gray-light;
+ background: @gray-lightest;
+ font-weight: normal;
+ vertical-align: top;
+}
+
+.Differences tbody td {
+ color: @text-color;
+ width: 50%;
+ .preformatted();
+ word-break: break-all;
+}
+
+.DifferencesSideBySide {
+ ins, del {
+ text-decoration: none;
+ }
+
+ .ChangeInsert {
+ td.Left {
+ background: @gray-lighter;
+ }
+ td.Right {
+ background: @color-diff-changed-new;
+ color: @color-diff-text-on-diff;
+ }
+ }
+
+ .ChangeDelete {
+ td.Left {
+ background: @color-diff-changed-old;
+ color: @color-diff-text-on-diff;
+ }
+ td.Right {
+ background: @gray-lighter;
+ }
+ }
+
+ .ChangeReplace {
+ td.Left {
+ background: @color-diff-changed-old;
+ color: @color-diff-text-on-diff;
+ del {
+ background: @color-diff-del;
+ }
+ }
+
+ td.Right {
+ background: @color-diff-changed-new;
+ color: @color-diff-text-on-diff;
+ ins {
+ background: @color-diff-ins;
+ }
+ }
+
+ }
+}
+
+.Differences .Skipped {
+ background: @gray-lightest;
+}
+
+.DifferencesInline .ChangeReplace .Left,
+.DifferencesInline .ChangeDelete .Left {
+ background: @color-diff-changed-old;
+}
+
+.DifferencesInline .ChangeReplace .Right,
+.DifferencesInline .ChangeInsert .Right {
+ background: @color-diff-green-shade-light;
+}
+
+.DifferencesInline .ChangeReplace ins {
+ background: @color-diff-pale-green;
+}
+
+.DifferencesInline .ChangeReplace del {
+ background: @color-change-replace-del;
+}
+
+.DifferencesInline {
+ tr td:last-child {
+ width: 90%;
+ }
+ tr th:first-child {
+ width: 5%;
+ }
+ tr th:nth-child(2) {
+ width: 5%;
+ }
+ ins, del {
+ text-decoration: none;
+ }
+}
+
+#layout.compact-layout, #layout.default-layout {
+ &.twocols table.Differences {
+ th {
+ font-size: 0.75em;
+ }
+ td {
+ font-size: 0.916em;
+ }
+ }
+}
diff --git a/vendor/gipfl/diff/src/HtmlRenderer/InlineDiff.php b/vendor/gipfl/diff/src/HtmlRenderer/InlineDiff.php
new file mode 100644
index 0000000..5df8bc0
--- /dev/null
+++ b/vendor/gipfl/diff/src/HtmlRenderer/InlineDiff.php
@@ -0,0 +1,10 @@
+<?php
+
+namespace gipfl\Diff\HtmlRenderer;
+
+use gipfl\Diff\PhpDiff\Renderer\Html\Inline;
+use ipl\Html\ValidHtml;
+
+class InlineDiff extends Inline implements ValidHtml
+{
+}
diff --git a/vendor/gipfl/diff/src/HtmlRenderer/SideBySideDiff.php b/vendor/gipfl/diff/src/HtmlRenderer/SideBySideDiff.php
new file mode 100644
index 0000000..e2ac5b2
--- /dev/null
+++ b/vendor/gipfl/diff/src/HtmlRenderer/SideBySideDiff.php
@@ -0,0 +1,10 @@
+<?php
+
+namespace gipfl\Diff\HtmlRenderer;
+
+use gipfl\Diff\PhpDiff\Renderer\Html\SideBySide;
+use ipl\Html\ValidHtml;
+
+class SideBySideDiff extends SideBySide implements ValidHtml
+{
+}
diff --git a/vendor/gipfl/diff/src/PhpDiff.php b/vendor/gipfl/diff/src/PhpDiff.php
new file mode 100644
index 0000000..da2cb1f
--- /dev/null
+++ b/vendor/gipfl/diff/src/PhpDiff.php
@@ -0,0 +1,147 @@
+<?php
+
+namespace gipfl\Diff;
+
+use gipfl\Diff\PhpDiff\OpCodeHelper;
+use gipfl\Diff\PhpDiff\SequenceMatcher;
+
+class PhpDiff
+{
+ /** @var array The "old" sequence to use as the basis for the comparison */
+ private $left;
+
+ /** @var array The "new" sequence to generate the changes for */
+ private $right;
+
+ /** @var array contains the generated opcodes for the differences between the two items */
+ private $groupedCodes;
+
+ /**
+ * @var array Associative array of the default options available for the diff class and their default value.
+ */
+ private $defaultOptions = [
+ 'context' => 3,
+ 'ignoreNewLines' => false,
+ 'ignoreWhitespace' => false,
+ 'ignoreCase' => false
+ ];
+
+ /**
+ * @var array Array of the options that have been applied for generating the diff.
+ */
+ private $options;
+
+ /**
+ * $left and $right can be strings, arrays of lines, null or any object that
+ * can be casted to a string
+ *
+ * @param mixed $left Left hand (old) side of the comparison
+ * @param mixed $right Right hand (new) side of the comparison
+ * @param array $options see $defaultOptions for possible settings
+ */
+ public function __construct($left, $right, array $options = [])
+ {
+ $this->setLeftLines($this->wantArray($left));
+ $this->setRightLines($this->wantArray($right));
+ $this->options = array_merge($this->defaultOptions, $options);
+ }
+
+ /**
+ * Get a range of lines from $start to $end from the first comparison string
+ * and return them as an array. If no values are supplied, the entire string
+ * is returned. It's also possible to specify just one line to return only
+ * that line.
+ *
+ * @param int $start The starting number.
+ * @param int $end The ending number. If not supplied, only the item in $start will be returned.
+ * @return array Array of all of the lines between the specified range.
+ */
+ public function getLeft($start = 0, $end = null)
+ {
+ if ($start === 0 && $end === null) {
+ return $this->left;
+ }
+
+ if ($end === null) {
+ $length = 1;
+ } else {
+ $length = $end - $start;
+ }
+
+ return array_slice($this->left, $start, $length);
+ }
+
+ /**
+ * Get a range of lines from $start to $end from the second comparison string
+ * and return them as an array. If no values are supplied, the entire string
+ * is returned. It's also possible to specify just one line to return only
+ * that line.
+ *
+ * @param int $start The starting number.
+ * @param int $end The ending number. If not supplied, only the item in $start will be returned.
+ * @return array Array of all of the lines between the specified range.
+ */
+ public function getRight($start = 0, $end = null)
+ {
+ if ($start === 0 && $end === null) {
+ return $this->right;
+ }
+
+ if ($end === null) {
+ $length = 1;
+ } else {
+ $length = $end - $start;
+ }
+
+ return array_slice($this->right, $start, $length);
+ }
+
+ /**
+ * Generate a list of the compiled and grouped opcodes for the differences between the
+ * two strings. Generally called by the renderer, this class instantiates the sequence
+ * matcher and performs the actual diff generation and return an array of the opcodes
+ * for it. Once generated, the results are cached in the diff class instance.
+ *
+ * @return array Array of the grouped opcodes for the generated diff.
+ */
+ public function getGroupedOpcodes()
+ {
+ if ($this->groupedCodes === null) {
+ $this->groupedCodes = $this->fetchGroupedOpCodes();
+ }
+
+ return $this->groupedCodes;
+ }
+
+ protected function fetchGroupedOpCodes()
+ {
+ $matcher = new SequenceMatcher($this->left, $this->right, null, $this->options);
+ return OpCodeHelper::getGroupedOpcodes(
+ $matcher->getOpcodes(),
+ $this->options['context']
+ );
+ }
+
+ protected function wantArray($value)
+ {
+ if (empty($value)) {
+ return [];
+ }
+ if (! is_array($value)) {
+ return explode("\n", (string) $value);
+ }
+
+ return $value;
+ }
+
+ protected function setLeftLines(array $lines)
+ {
+ $this->left = $lines;
+ }
+
+ protected function setRightLines(array $lines)
+ {
+ $this->right = $lines;
+ }
+
+}
diff --git a/vendor/gipfl/diff/src/PhpDiff/ArrayHelper.php b/vendor/gipfl/diff/src/PhpDiff/ArrayHelper.php
new file mode 100644
index 0000000..2c57c8d
--- /dev/null
+++ b/vendor/gipfl/diff/src/PhpDiff/ArrayHelper.php
@@ -0,0 +1,54 @@
+<?php
+
+namespace gipfl\Diff\PhpDiff;
+
+abstract class ArrayHelper
+{
+ /**
+ * Helper function that provides the ability to return the value for a key
+ * in an array of it exists, or if it doesn't then return a default value.
+ * Essentially cleaner than doing a series of if(isset()) {} else {} calls.
+ *
+ * @param array $array The array to search.
+ * @param string $key The key to check that exists.
+ * @param mixed $default The value to return as the default value if the key doesn't exist.
+ * @return mixed The value from the array if the key exists or otherwise the default.
+ */
+ public static function getPropertyOrDefault($array, $key, $default)
+ {
+ if (isset($array[$key])) {
+ return $array[$key];
+ }
+
+ return $default;
+ }
+
+ /**
+ * Sort an array by the nested arrays it contains. Helper function for getMatchingBlocks
+ *
+ * @param array $a First array to compare.
+ * @param array $b Second array to compare.
+ * @return int -1, 0 or 1, as expected by the usort function.
+ */
+ public static function tupleSort($a, $b)
+ {
+ $max = max(count($a), count($b));
+ for ($i = 0; $i < $max; ++$i) {
+ if ($a[$i] < $b[$i]) {
+ return -1;
+ }
+ if ($a[$i] > $b[$i]) {
+ return 1;
+ }
+ }
+
+ if (count($a) === count($b)) {
+ return 0;
+ }
+ if (count($a) < count($b)) {
+ return -1;
+ }
+
+ return 1;
+ }
+}
diff --git a/vendor/gipfl/diff/src/PhpDiff/OpCodeHelper.php b/vendor/gipfl/diff/src/PhpDiff/OpCodeHelper.php
new file mode 100644
index 0000000..7b12b1e
--- /dev/null
+++ b/vendor/gipfl/diff/src/PhpDiff/OpCodeHelper.php
@@ -0,0 +1,144 @@
+<?php
+
+namespace gipfl\Diff\PhpDiff;
+
+use function count;
+use function max;
+use function min;
+
+abstract class OpCodeHelper
+{
+ /**
+ * Return a list of all of the opcodes for the differences between the
+ * two strings.
+ *
+ * The nested array returned contains an array describing the opcode
+ * which includes:
+ * 0 - The type of tag (as described below) for the opcode.
+ * 1 - The beginning line in the first sequence.
+ * 2 - The end line in the first sequence.
+ * 3 - The beginning line in the second sequence.
+ * 4 - The end line in the second sequence.
+ *
+ * The different types of tags include:
+ * replace - The string from $i1 to $i2 in $a should be replaced by
+ * the string in $b from $j1 to $j2.
+ * delete - The string in $a from $i1 to $j2 should be deleted.
+ * insert - The string in $b from $j1 to $j2 should be inserted at
+ * $i1 in $a.
+ * equal - The two strings with the specified ranges are equal.
+ *
+ * @param array $blocks
+ * @return array Array of the opcodes describing the differences between the strings.
+ */
+ public static function calculateOpCodes(array $blocks)
+ {
+ $lastLeftEnd = 0;
+ $lastRightEnd = 0;
+ $opCodes = [];
+
+ foreach ($blocks as list($beginLeft, $beginRight, $cntLines)) {
+ $tag = null;
+ if ($lastLeftEnd < $beginLeft) {
+ if ($lastRightEnd < $beginRight) {
+ $tag = 'replace';
+ } else {
+ $tag = 'delete';
+ }
+ } elseif ($lastRightEnd < $beginRight) {
+ $tag = 'insert';
+ }
+
+ if ($tag) {
+ $opCodes[] = [$tag, $lastLeftEnd, $beginLeft, $lastRightEnd, $beginRight];
+ }
+
+ $lastLeftEnd = $beginLeft + $cntLines;
+ $lastRightEnd = $beginRight + $cntLines;
+
+ if ($cntLines) {
+ $opCodes[] = ['equal', $beginLeft, $lastLeftEnd, $beginRight, $lastRightEnd];
+ }
+ }
+
+ return $opCodes;
+ }
+
+ /**
+ * Return a series of nested arrays containing different groups of generated
+ * opcodes for the differences between the strings with up to $context lines
+ * of surrounding content.
+ *
+ * Essentially what happens here is any big equal blocks of strings are stripped
+ * out, the smaller subsets of changes are then arranged in to their groups.
+ * This means that the sequence matcher and diffs do not need to include the full
+ * content of the different files but can still provide context as to where the
+ * changes are.
+ *
+ * @param array $opCodes
+ * @param int $context The number of lines of context to provide around the groups.
+ * @return array Nested array of all of the grouped opcodes.
+ */
+ public static function getGroupedOpcodes(array $opCodes, $context = 3)
+ {
+ if (empty($opCodes)) {
+ $opCodes = [
+ ['equal', 0, 1, 0, 1]
+ ];
+ }
+
+ if ($opCodes[0][0] === 'equal') {
+ $opCodes[0] = [
+ $opCodes[0][0],
+ max($opCodes[0][1], $opCodes[0][2] - $context),
+ $opCodes[0][2],
+ max($opCodes[0][3], $opCodes[0][4] - $context),
+ $opCodes[0][4]
+ ];
+ }
+
+ $lastItem = count($opCodes) - 1;
+ if ($opCodes[$lastItem][0] === 'equal') {
+ list($tag, $beginLeft, $endLeft, $beginRight, $endRight) = $opCodes[$lastItem];
+ $opCodes[$lastItem] = [
+ $tag,
+ $beginLeft,
+ min($endLeft, $beginLeft + $context),
+ $beginRight,
+ min($endRight, $beginRight + $context)
+ ];
+ }
+ /*
+ public $type;
+ public $beginLeft;
+ public $endLeft;
+ public $beginRight;
+ public $endRight;
+ */
+ $maxRange = $context * 2;
+ $groups = [];
+ $group = [];
+ foreach ($opCodes as list($tag, $beginLeft, $endLeft, $beginRight, $endRight)) {
+ if ($tag === 'equal' && $endLeft - $beginLeft > $maxRange) {
+ $group[] = [
+ $tag,
+ $beginLeft,
+ min($endLeft, $beginLeft + $context),
+ $beginRight,
+ min($endRight, $beginRight + $context)
+ ];
+ $groups[] = $group;
+ $group = [];
+ $beginLeft = max($beginLeft, $endLeft - $context);
+ $beginRight = max($beginRight, $endRight - $context);
+ }
+ $group[] = [$tag, $beginLeft, $endLeft, $beginRight, $endRight];
+ }
+
+ if (!empty($group) && !(count($group) === 1 && $group[0][0] === 'equal')) {
+ $groups[] = $group;
+ }
+
+ return $groups;
+ }
+}
diff --git a/vendor/gipfl/diff/src/PhpDiff/Ratio.php b/vendor/gipfl/diff/src/PhpDiff/Ratio.php
new file mode 100644
index 0000000..4db1460
--- /dev/null
+++ b/vendor/gipfl/diff/src/PhpDiff/Ratio.php
@@ -0,0 +1,139 @@
+<?php
+
+namespace gipfl\Diff\PhpDiff;
+
+use function count;
+
+class Ratio
+{
+ /**
+ * @var SequenceMatcher
+ */
+ private $matcher;
+
+ /** @var float */
+ private $ratio;
+
+ /** @var array */
+ private $a;
+
+ /** @var array */
+ private $b;
+
+ /** @var array */
+ private $fullBCount;
+
+ public function __construct(SequenceMatcher $matcher)
+ {
+ $this->matcher = $matcher;
+ $this->a = $matcher->getLeftSequence();
+ $this->b = $matcher->getRightSequence();
+ }
+
+ /**
+ * Return a measure of the similarity between the two sequences.
+ * This will be a float value between 0 and 1.
+ *
+ * Out of all of the ratio calculation functions, this is the most
+ * expensive to call if getMatchingBlocks or getOpCodes is yet to be
+ * called. The other calculation methods (quickRatio and realquickRatio)
+ * can be used to perform quicker calculations but may be less accurate.
+ *
+ * The ratio is calculated as (2 * number of matches) / total number of
+ * elements in both sequences.
+ *
+ * @return float The calculated ratio.
+ */
+ public function getRatio()
+ {
+ if ($this->ratio === null) {
+ $matcher = $this->matcher;
+ $matches = array_reduce($matcher->getMatchingBlocks(), [$this, 'ratioReduce'], 0);
+ $this->ratio = $this->calculateRatio(
+ $matches,
+ count($this->a) + count($this->b)
+ );
+ }
+
+ return $this->ratio;
+ }
+
+ /**
+ * Helper function to calculate the number of matches for Ratio().
+ *
+ * @param int $sum The running total for the number of matches.
+ * @param array $triple Array containing the matching block triple to add to the running total.
+ * @return int The new running total for the number of matches.
+ */
+ private function ratioReduce($sum, $triple)
+ {
+ return $sum + ($triple[count($triple) - 1]);
+ }
+
+ /**
+ * Quickly return an upper bound ratio for the similarity of the strings.
+ * This is quicker to compute than Ratio().
+ *
+ * @return float The calculated ratio.
+ */
+ private function quickRatio()
+ {
+ $aLength = count($this->a);
+ $bLength = count($this->b);
+ if ($this->fullBCount === null) {
+ $this->fullBCount = [];
+ for ($i = 0; $i < $bLength; ++$i) {
+ $char = $this->b[$i];
+ $this->fullBCount[$char] = ArrayHelper::getPropertyOrDefault($this->fullBCount, $char, 0) + 1;
+ }
+ }
+
+ $avail = array();
+ $matches = 0;
+ for ($i = 0; $i < $aLength; ++$i) {
+ $char = $this->a[$i];
+ if (isset($avail[$char])) {
+ $numb = $avail[$char];
+ } else {
+ $numb = ArrayHelper::getPropertyOrDefault($this->fullBCount, $char, 0);
+ }
+ $avail[$char] = $numb - 1;
+ if ($numb > 0) {
+ ++$matches;
+ }
+ }
+
+ $this->calculateRatio($matches, $aLength + $bLength);
+ }
+
+ /**
+ * Return an upper bound ratio really quickly for the similarity of the strings.
+ * This is quicker to compute than Ratio() and quickRatio().
+ *
+ * @return float The calculated ratio.
+ */
+ private function realquickRatio()
+ {
+ $aLength = count($this->a);
+ $bLength = count($this->b);
+
+ return $this->calculateRatio(min($aLength, $bLength), $aLength + $bLength);
+ }
+
+ /**
+ * Helper function for calculating the ratio to measure similarity for the strings.
+ * The ratio is defined as being 2 * (number of matches / total length)
+ *
+ * @param int $matches The number of matches in the two strings.
+ * @param int $length The length of the two strings.
+ * @return float The calculated ratio.
+ */
+ private function calculateRatio($matches, $length = 0)
+ {
+ if ($length) {
+ return 2 * ($matches / $length);
+ }
+
+ return 1;
+ }
+}
diff --git a/vendor/gipfl/diff/src/PhpDiff/Renderer/AbstractRenderer.php b/vendor/gipfl/diff/src/PhpDiff/Renderer/AbstractRenderer.php
new file mode 100644
index 0000000..fc93d8d
--- /dev/null
+++ b/vendor/gipfl/diff/src/PhpDiff/Renderer/AbstractRenderer.php
@@ -0,0 +1,44 @@
+<?php
+
+namespace gipfl\Diff\PhpDiff\Renderer;
+
+use gipfl\Diff\PhpDiff;
+
+/**
+ * Abstract class for diff renderers in PHP DiffLib.
+ */
+abstract class AbstractRenderer
+{
+ /** @var PhpDiff */
+ public $diff;
+
+ /** @var array default options that apply to this renderer */
+ protected $defaultOptions = [];
+
+ /** @var array merged (user applied and default) options for the renderer */
+ protected $options = [];
+
+ /**
+ * @param PhpDiff $diff
+ * @param array $options Optionally, an array of the options for the renderer.
+ */
+ public function __construct(PhpDiff $diff, array $options = [])
+ {
+ $this->diff = $diff;
+ $this->setOptions($options);
+ }
+
+ /**
+ * Set the options of the renderer to those supplied in the passed in array.
+ * Options are merged with the default to ensure that there aren't any missing
+ * options.
+ *
+ * @param array $options
+ */
+ public function setOptions(array $options)
+ {
+ $this->options = array_merge($this->defaultOptions, $options);
+ }
+
+ abstract public function render();
+}
diff --git a/vendor/gipfl/diff/src/PhpDiff/Renderer/Html/ArrayRenderer.php b/vendor/gipfl/diff/src/PhpDiff/Renderer/Html/ArrayRenderer.php
new file mode 100644
index 0000000..57f6cb4
--- /dev/null
+++ b/vendor/gipfl/diff/src/PhpDiff/Renderer/Html/ArrayRenderer.php
@@ -0,0 +1,207 @@
+<?php
+
+namespace gipfl\Diff\PhpDiff\Renderer\Html;
+
+use gipfl\Diff\PhpDiff\Renderer\AbstractRenderer;
+
+/**
+ * Base renderer for rendering HTML based diffs for PHP DiffLib.
+ */
+class ArrayRenderer extends AbstractRenderer
+{
+ /** @var array default options */
+ protected $defaultOptions = [
+ 'tabSize' => 4
+ ];
+
+ /**
+ * Render and return an array structure suitable for generating HTML
+ * based differences. Generally called by subclasses that generate a
+ * HTML based diff and return an array of the changes to show in the diff.
+ *
+ * @return array An array of the generated chances, suitable for presentation in HTML.
+ */
+ public function render()
+ {
+ // As we'll be modifying a & b to include our change markers,
+ // we need to get the contents and store them here. That way
+ // we're not going to destroy the original data
+ $a = $this->diff->getLeft();
+ $b = $this->diff->getRight();
+
+ $changes = [];
+ foreach ($this->diff->getGroupedOpcodes() as $group) {
+ $changes[] = $this->renderOpcodesGroup($group, $a, $b);
+ }
+ return $changes;
+ }
+
+ protected function insertLineMarkers($line, $start, $end)
+ {
+ $last = $end + mb_strlen($line);
+
+ return mb_substr($line, 0, $start)
+ . "\0"
+ . mb_substr($line, $start, $last - $start)
+ . "\1"
+ . mb_substr($line, $last);
+ }
+
+ /**
+ * @param $group
+ * @param array $a
+ * @param array $b
+ * @return array
+ */
+ protected function renderOpcodesGroup($group, array $a, array $b)
+ {
+ $blocks = [];
+ $lastTag = null;
+ $lastBlock = 0;
+ foreach ($group as list($tag, $i1, $i2, $j1, $j2)) {
+ if ($tag === 'replace' && $i2 - $i1 === $j2 - $j1) {
+ for ($i = 0; $i < ($i2 - $i1); ++$i) {
+ $fromLine = $a[$i1 + $i];
+ $toLine = $b[$j1 + $i];
+
+ list($start, $end) = $this->getChangeExtent($fromLine, $toLine);
+ if ($start !== 0 || $end !== 0) {
+ $a[$i1 + $i] = $this->insertLineMarkers($fromLine, $start, $end);
+ $b[$j1 + $i] = $this->insertLineMarkers($toLine, $start, $end);
+ }
+ }
+ }
+
+ if ($tag !== $lastTag) {
+ $blocks[] = [
+ 'tag' => $tag,
+ 'base' => [
+ 'offset' => $i1,
+ 'lines' => []
+ ],
+ 'changed' => [
+ 'offset' => $j1,
+ 'lines' => []
+ ]
+ ];
+ $lastBlock = count($blocks) - 1;
+ }
+
+ $lastTag = $tag;
+
+ if ($tag === 'equal') {
+ $lines = array_slice($a, $i1, ($i2 - $i1));
+ $blocks[$lastBlock]['base']['lines'] += $this->formatLines($lines);
+ $lines = array_slice($b, $j1, ($j2 - $j1));
+ $blocks[$lastBlock]['changed']['lines'] += $this->formatLines($lines);
+ } else {
+ if ($tag === 'replace' || $tag === 'delete') {
+ $lines = array_slice($a, $i1, ($i2 - $i1));
+ $lines = $this->formatLines($lines);
+ $lines = str_replace(array("\0", "\1"), array('<del>', '</del>'), $lines);
+ $blocks[$lastBlock]['base']['lines'] += $lines;
+ }
+
+ if ($tag === 'replace' || $tag === 'insert') {
+ $lines = array_slice($b, $j1, ($j2 - $j1));
+ $lines = $this->formatLines($lines);
+ $lines = str_replace(array("\0", "\1"), array('<ins>', '</ins>'), $lines);
+ $blocks[$lastBlock]['changed']['lines'] += $lines;
+ }
+ }
+ }
+
+ return $blocks;
+ }
+
+ /**
+ * Given two strings, determine where the changes in the two strings
+ * begin, and where the changes in the two strings end.
+ *
+ * @param string $fromLine The first string.
+ * @param string $toLine The second string.
+ * @return array Array containing the starting position (0 by default) and the ending position (-1 by default)
+ */
+ private function getChangeExtent($fromLine, $toLine)
+ {
+ $start = 0;
+ $limit = min(strlen($fromLine), strlen($toLine));
+ while ($start < $limit && $fromLine[$start] === $toLine[$start]) {
+ ++$start;
+ }
+ $end = -1;
+ $limit -= $start;
+ /** @noinspection SubStrUsedAsArrayAccessInspection $end is negative, array index needs PHP >= 7 */
+ while (-$end <= $limit && substr($fromLine, $end, 1) === substr($toLine, $end, 1)) {
+ --$end;
+ }
+ return [
+ $start,
+ $end + 1
+ ];
+ }
+
+ /**
+ * Format a series of lines suitable for output in a HTML rendered diff.
+ * This involves replacing tab characters with spaces, making the HTML safe
+ * for output, ensuring that double spaces are replaced with &nbsp; etc.
+ *
+ * @param array $lines lines to format.
+ * @return array formatted lines.
+ */
+ protected function formatLines($lines)
+ {
+ $lines = array_map([$this, 'ExpandTabs'], $lines);
+ $lines = array_map([$this, 'HtmlSafe'], $lines);
+ foreach ($lines as &$line) {
+ $line = preg_replace_callback('# ( +)|^ #', [$this, 'fixSpaces'], $line);
+ }
+ return $lines;
+ }
+
+ /**
+ * Replace a string containing spaces with a HTML representation using &nbsp;.
+ *
+ * @param string[] $matches preg matches.
+ * @return string HTML representation of the string.
+ */
+ private function fixSpaces(array $matches)
+ {
+ $count = 0;
+
+ if (count($matches) > 1) {
+ $spaces = $matches[1];
+ $count = strlen($spaces);
+ }
+
+ if ($count === 0) {
+ return '';
+ }
+
+ $div = floor($count / 2);
+ $mod = $count % 2;
+ return str_repeat('&nbsp; ', $div).str_repeat('&nbsp;', $mod);
+ }
+
+ /**
+ * Replace tabs in a single line with a number of spaces as defined by the tabSize option.
+ *
+ * @param string $line The containing tabs to convert.
+ * @return string The line with the tabs converted to spaces.
+ */
+ private function expandTabs($line)
+ {
+ return str_replace("\t", str_repeat(' ', $this->options['tabSize']), $line);
+ }
+
+ /**
+ * Make a string containing HTML safe for output on a page.
+ *
+ * @param string $string The string.
+ * @return string The string with the HTML characters replaced by entities.
+ */
+ private function htmlSafe($string)
+ {
+ return htmlspecialchars($string, ENT_NOQUOTES, 'UTF-8');
+ }
+}
diff --git a/vendor/gipfl/diff/src/PhpDiff/Renderer/Html/Inline.php b/vendor/gipfl/diff/src/PhpDiff/Renderer/Html/Inline.php
new file mode 100644
index 0000000..6587de1
--- /dev/null
+++ b/vendor/gipfl/diff/src/PhpDiff/Renderer/Html/Inline.php
@@ -0,0 +1,104 @@
+<?php
+
+namespace gipfl\Diff\PhpDiff\Renderer\Html;
+
+/**
+ * Inline HTML diff generator for PHP DiffLib.
+ */
+class Inline extends ArrayRenderer
+{
+ /**
+ * Render a and return diff with changes between the two sequences
+ * displayed inline (under each other)
+ *
+ * @return string The generated inline diff.
+ */
+ public function render()
+ {
+ $changes = parent::render();
+ $html = '';
+ if (empty($changes)) {
+ return $html;
+ }
+
+ $html .= '<table class="Differences DifferencesInline">';
+ $html .= '<thead>';
+ $html .= '<tr>';
+ $html .= '<th>Old</th>';
+ $html .= '<th>New</th>';
+ $html .= '<th>Differences</th>';
+ $html .= '</tr>';
+ $html .= '</thead>';
+ foreach ($changes as $i => $blocks) {
+ // If this is a separate block, we're condensing code so output ...,
+ // indicating a significant portion of the code has been collapsed as
+ // it is the same
+ if ($i > 0) {
+ $html .= '<tbody class="Skipped">';
+ $html .= '<th>&hellip;</th>';
+ $html .= '<th>&hellip;</th>';
+ $html .= '<td>&nbsp;</td>';
+ $html .= '</tbody>';
+ }
+
+ foreach ($blocks as $change) {
+ $html .= '<tbody class="Change'.ucfirst($change['tag']).'">';
+ // Equal changes should be shown on both sides of the diff
+ if ($change['tag'] === 'equal') {
+ foreach ($change['base']['lines'] as $no => $line) {
+ $fromLine = $change['base']['offset'] + $no + 1;
+ $toLine = $change['changed']['offset'] + $no + 1;
+ $html .= '<tr>';
+ $html .= '<th>' . $fromLine . '</th>';
+ $html .= '<th>' . $toLine . '</th>';
+ $html .= '<td class="Left">' . $line . '</td>';
+ $html .= '</tr>';
+ }
+ } elseif ($change['tag'] === 'insert') {
+ // Added lines only on the right side
+ foreach ($change['changed']['lines'] as $no => $line) {
+ $toLine = $change['changed']['offset'] + $no + 1;
+ $html .= '<tr>';
+ $html .= '<th>&nbsp;</th>';
+ $html .= '<th>' . $toLine . '</th>';
+ $html .= '<td class="Right"><ins>' . $line . '</ins>&nbsp;</td>';
+ $html .= '</tr>';
+ }
+ } elseif ($change['tag'] === 'delete') {
+ // Show deleted lines only on the left side
+ foreach ($change['base']['lines'] as $no => $line) {
+ $fromLine = $change['base']['offset'] + $no + 1;
+ $html .= '<tr>';
+ $html .= '<th>' . $fromLine . '</th>';
+ $html .= '<th>&nbsp;</th>';
+ $html .= '<td class="Left"><del>' . $line . '</del>&nbsp;</td>';
+ $html .= '</tr>';
+ }
+ } elseif ($change['tag'] === 'replace') {
+ // Show modified lines on both sides
+ foreach ($change['base']['lines'] as $no => $line) {
+ $fromLine = $change['base']['offset'] + $no + 1;
+ $html .= '<tr>';
+ $html .= '<th>' . $fromLine . '</th>';
+ $html .= '<th>&nbsp;</th>';
+ $html .= '<td class="Left"><span>' . $line . '</span></td>';
+ $html .= '</tr>';
+ }
+
+ foreach ($change['changed']['lines'] as $no => $line) {
+ $toLine = $change['changed']['offset'] + $no + 1;
+ $html .= '<tr>';
+ $html .= '<th>&nbsp;</th>';
+ $html .= '<th>' . $toLine . '</th>';
+ $html .= '<td class="Right"><span>' . $line . '</span></td>';
+ $html .= '</tr>';
+ }
+ }
+ $html .= '</tbody>';
+ }
+ }
+ $html .= '</table>';
+
+ return $html;
+ }
+}
diff --git a/vendor/gipfl/diff/src/PhpDiff/Renderer/Html/SideBySide.php b/vendor/gipfl/diff/src/PhpDiff/Renderer/Html/SideBySide.php
new file mode 100644
index 0000000..2d16e08
--- /dev/null
+++ b/vendor/gipfl/diff/src/PhpDiff/Renderer/Html/SideBySide.php
@@ -0,0 +1,121 @@
+<?php
+
+namespace gipfl\Diff\PhpDiff\Renderer\Html;
+
+/**
+ * Side by Side HTML diff generator for PHP DiffLib.
+ */
+class SideBySide extends ArrayRenderer
+{
+ /**
+ * Render a and return diff with changes between the two sequences
+ * displayed side by side.
+ *
+ * @return string The generated side by side diff.
+ */
+ public function render()
+ {
+ $changes = parent::render();
+
+ $html = '';
+ if (empty($changes)) {
+ return $html;
+ }
+
+ $html .= '<table class="Differences DifferencesSideBySide">';
+ $html .= '<thead>';
+ $html .= '<tr>';
+ $html .= '<th colspan="2">Old Version</th>';
+ $html .= '<th colspan="2">New Version</th>';
+ $html .= '</tr>';
+ $html .= '</thead>';
+ foreach ($changes as $i => $blocks) {
+ if ($i > 0) {
+ $html .= '<tbody class="Skipped">';
+ $html .= '<th>&hellip;</th><td>&nbsp;</td>';
+ $html .= '<th>&hellip;</th><td>&nbsp;</td>';
+ $html .= '</tbody>';
+ }
+
+ foreach ($blocks as $change) {
+ $html .= '<tbody class="Change'.ucfirst($change['tag']).'">';
+ // Equal changes should be shown on both sides of the diff
+ if ($change['tag'] === 'equal') {
+ foreach ($change['base']['lines'] as $no => $line) {
+ $fromLine = $change['base']['offset'] + $no + 1;
+ $toLine = $change['changed']['offset'] + $no + 1;
+ $html .= '<tr>';
+ $html .= '<th>' . $fromLine . '</th>';
+ $html .= '<td class="Left"><span>' . $line . '</span>&nbsp;</td>';
+ $html .= '<th>' . $toLine . '</th>';
+ $html .= '<td class="Right"><span>' . $line . '</span>&nbsp;</td>';
+ $html .= '</tr>';
+ }
+ } elseif ($change['tag'] === 'insert') {
+ // Added lines only on the right side
+ foreach ($change['changed']['lines'] as $no => $line) {
+ $toLine = $change['changed']['offset'] + $no + 1;
+ $html .= '<tr>';
+ $html .= '<th>&nbsp;</th>';
+ $html .= '<td class="Left">&nbsp;</td>';
+ $html .= '<th>' . $toLine . '</th>';
+ $html .= '<td class="Right"><ins>' . $line . '</ins>&nbsp;</td>';
+ $html .= '</tr>';
+ }
+ } elseif ($change['tag'] === 'delete') {
+ // Show deleted lines only on the left side
+ foreach ($change['base']['lines'] as $no => $line) {
+ $fromLine = $change['base']['offset'] + $no + 1;
+ $html .= '<tr>';
+ $html .= '<th>' . $fromLine . '</th>';
+ $html .= '<td class="Left"><del>' . $line . '</del>&nbsp;</td>';
+ $html .= '<th>&nbsp;</th>';
+ $html .= '<td class="Right">&nbsp;</td>';
+ $html .= '</tr>';
+ }
+ } elseif ($change['tag'] === 'replace') {
+ // Show modified lines on both sides
+ if (count($change['base']['lines']) >= count($change['changed']['lines'])) {
+ foreach ($change['base']['lines'] as $no => $line) {
+ $fromLine = $change['base']['offset'] + $no + 1;
+ $html .= '<tr>';
+ $html .= '<th>'.$fromLine.'</th>';
+ $html .= '<td class="Left"><span>' . $line . '</span>&nbsp;</td>';
+ if (!isset($change['changed']['lines'][$no])) {
+ $toLine = '&nbsp;';
+ $changedLine = '&nbsp;';
+ } else {
+ $toLine = $change['base']['offset'] + $no + 1;
+ $changedLine = '<span>'.$change['changed']['lines'][$no].'</span>';
+ }
+ $html .= '<th>' . $toLine . '</th>';
+ $html .= '<td class="Right">' . $changedLine . '</td>';
+ $html .= '</tr>';
+ }
+ } else {
+ foreach ($change['changed']['lines'] as $no => $changedLine) {
+ if (!isset($change['base']['lines'][$no])) {
+ $fromLine = '&nbsp;';
+ $line = '&nbsp;';
+ } else {
+ $fromLine = $change['base']['offset'] + $no + 1;
+ $line = '<span>' . $change['base']['lines'][$no] . '</span>';
+ }
+ $html .= '<tr>';
+ $html .= '<th>' . $fromLine . '</th>';
+ $html .= '<td class="Left"><span>' . $line . '</span>&nbsp;</td>';
+ $toLine = $change['changed']['offset'] + $no + 1;
+ $html .= '<th>' . $toLine . '</th>';
+ $html .= '<td class="Right">' . $changedLine . '</td>';
+ $html .= '</tr>';
+ }
+ }
+ }
+ $html .= '</tbody>';
+ }
+ }
+ $html .= '</table>';
+
+ return $html;
+ }
+}
diff --git a/vendor/gipfl/diff/src/PhpDiff/Renderer/Text/Context.php b/vendor/gipfl/diff/src/PhpDiff/Renderer/Text/Context.php
new file mode 100644
index 0000000..b8d9cad
--- /dev/null
+++ b/vendor/gipfl/diff/src/PhpDiff/Renderer/Text/Context.php
@@ -0,0 +1,98 @@
+<?php
+
+namespace gipfl\Diff\PhpDiff\Renderer\Text;
+
+use gipfl\Diff\PhpDiff\Renderer\AbstractRenderer;
+
+/**
+ * Context diff generator for PHP DiffLib.
+ */
+class Context extends AbstractRenderer
+{
+ /**
+ * @var array Array of the different opcode tags and how they map to the context diff equivalent.
+ */
+ private $tagMap = [
+ 'insert' => '+',
+ 'delete' => '-',
+ 'replace' => '!',
+ 'equal' => ' '
+ ];
+
+ /**
+ * Render and return a context formatted (old school!) diff file.
+ *
+ * @return string The generated context diff.
+ */
+ public function render()
+ {
+ $diff = '';
+ $opCodes = $this->diff->getGroupedOpcodes();
+ foreach ($opCodes as $group) {
+ $diff .= "***************\n";
+ $lastItem = count($group)-1;
+ $i1 = $group[0][1];
+ $i2 = $group[$lastItem][2];
+ $j1 = $group[0][3];
+ $j2 = $group[$lastItem][4];
+
+ if ($i2 - $i1 >= 2) {
+ $diff .= '*** '.($group[0][1] + 1).','.$i2." ****\n";
+ } else {
+ $diff .= '*** '.$i2." ****\n";
+ }
+
+ if ($j2 - $j1 >= 2) {
+ $separator = '--- '.($j1 + 1).','.$j2." ----\n";
+ } else {
+ $separator = '--- '.$j2." ----\n";
+ }
+
+ $hasVisible = false;
+ foreach ($group as $code) {
+ if ($code[0] === 'replace' || $code[0] === 'delete') {
+ $hasVisible = true;
+ break;
+ }
+ }
+
+ if ($hasVisible) {
+ foreach ($group as $code) {
+ list($tag, $i1, $i2, $j1, $j2) = $code;
+ if ($tag === 'insert') {
+ continue;
+ }
+ $diff .= $this->tagMap[$tag]
+ . ' '
+ . implode("\n" . $this->tagMap[$tag] . ' ', $this->diff->getLeft($i1, $i2))
+ . "\n";
+ }
+ }
+
+ $hasVisible = false;
+ foreach ($group as $code) {
+ if ($code[0] === 'replace' || $code[0] === 'insert') {
+ $hasVisible = true;
+ break;
+ }
+ }
+
+ $diff .= $separator;
+
+ if ($hasVisible) {
+ foreach ($group as $code) {
+ list($tag, $i1, $i2, $j1, $j2) = $code;
+ if ($tag === 'delete') {
+ continue;
+ }
+ $diff .= $this->tagMap[$tag]
+ . ' '
+ . implode("\n" . $this->tagMap[$tag] . ' ', $this->diff->getRight($j1, $j2))
+ . "\n";
+ }
+ }
+ }
+
+ return $diff;
+ }
+}
diff --git a/vendor/gipfl/diff/src/PhpDiff/Renderer/Text/Unified.php b/vendor/gipfl/diff/src/PhpDiff/Renderer/Text/Unified.php
new file mode 100644
index 0000000..afeb96d
--- /dev/null
+++ b/vendor/gipfl/diff/src/PhpDiff/Renderer/Text/Unified.php
@@ -0,0 +1,52 @@
+<?php
+
+namespace gipfl\Diff\PhpDiff\Renderer\Text;
+
+use gipfl\Diff\PhpDiff\Renderer\AbstractRenderer;
+
+/**
+ * Unified diff generator for PHP DiffLib.
+ */
+class Unified extends AbstractRenderer
+{
+ /**
+ * Render and return a unified diff.
+ *
+ * @return string The unified diff.
+ */
+ public function render()
+ {
+ $diff = '';
+ $opCodes = $this->diff->getGroupedOpcodes();
+ foreach ($opCodes as $group) {
+ $lastItem = count($group)-1;
+ $i1 = $group[0][1];
+ $i2 = $group[$lastItem][2];
+ $j1 = $group[0][3];
+ $j2 = $group[$lastItem][4];
+
+ if ($i1 === 0 && $i2 === 0) {
+ $i1 = -1;
+ $i2 = -1;
+ }
+
+ $diff .= '@@ -' . ($i1 + 1) . ',' . ($i2 - $i1) . ' +' . ($j1 + 1) . ',' . ($j2 - $j1) . " @@\n";
+ foreach ($group as $code) {
+ list($tag, $i1, $i2, $j1, $j2) = $code;
+ if ($tag === 'equal') {
+ $diff .= ' ' . implode("\n ", $this->diff->getLeft($i1, $i2))."\n";
+ } else {
+ if ($tag === 'replace' || $tag === 'delete') {
+ $diff .= '-' . implode("\n-", $this->diff->getLeft($i1, $i2))."\n";
+ }
+
+ if ($tag === 'replace' || $tag === 'insert') {
+ $diff .= '+' . implode("\n+", $this->diff->getRight($j1, $j2))."\n";
+ }
+ }
+ }
+ }
+
+ return $diff;
+ }
+}
diff --git a/vendor/gipfl/diff/src/PhpDiff/SequenceMatcher.php b/vendor/gipfl/diff/src/PhpDiff/SequenceMatcher.php
new file mode 100644
index 0000000..a185762
--- /dev/null
+++ b/vendor/gipfl/diff/src/PhpDiff/SequenceMatcher.php
@@ -0,0 +1,438 @@
+<?php
+
+namespace gipfl\Diff\PhpDiff;
+
+use function array_merge;
+use function count;
+use function is_array;
+use function str_replace;
+use function str_split;
+use function strtolower;
+
+/**
+ * Sequence matcher for Diff
+ */
+class SequenceMatcher
+{
+ /**
+ * Either a string or an array containing a callback function to determine
+ * if a line is "junk" or not
+ *
+ * @var string|array
+ */
+ private $junkCallback;
+
+ /**
+ * @var array The first sequence to compare against.
+ */
+ private $left = [];
+
+ /**
+ * @var array The second sequence.
+ */
+ private $right = [];
+
+ /**
+ * @var array Characters that are considered junk from the second sequence. Characters are the array key.
+ */
+ private $junkCharacters = [];
+
+ /**
+ * @var array Array of indices that do not contain junk elements.
+ */
+ private $b2j = [];
+
+ private $options = [];
+
+ private $defaultOptions = [
+ 'ignoreNewLines' => false,
+ 'ignoreWhitespace' => false,
+ 'ignoreCase' => false
+ ];
+
+ /** @var array|null */
+ private $matchingBlocks;
+
+ /** @var array|null */
+ private $opCodes;
+
+ /**
+ * The constructor. With the sequences being passed, they'll be set for the
+ * sequence matcher and it will perform a basic cleanup & calculate junk
+ * elements.
+ *
+ * @param string|array $left A string or array containing the lines to compare against.
+ * @param string|array $right A string or array containing the lines to compare.
+ * @param string|array $junkCallback Either an array or string that references a callback
+ * function (if there is one) to determine 'junk' characters.
+ * @param array $options
+ */
+ public function __construct($left, $right, $junkCallback = null, $options = [])
+ {
+ $this->junkCallback = $junkCallback;
+ $this->setOptions($options);
+ $this->setSequences($left, $right);
+ }
+
+ public function setOptions($options)
+ {
+ $this->options = array_merge($this->defaultOptions, $options);
+ }
+
+ /**
+ * Set the first and second sequences to use with the sequence matcher.
+ *
+ * @param string|array $left A string or array containing the lines to compare against.
+ * @param string|array $right A string or array containing the lines to compare.
+ */
+ public function setSequences($left, $right)
+ {
+ $this->setLeftSequence($left);
+ $this->setRightSequence($right);
+ }
+
+ /**
+ * Set the first sequence and reset any internal caches to indicate that
+ * when calling the calculation methods, we need to recalculate them.
+ *
+ * @param string|array $sequence The sequence to set as the first sequence.
+ */
+ protected function setLeftSequence($sequence)
+ {
+ if (!is_array($sequence)) {
+ $sequence = str_split($sequence);
+ }
+ if ($sequence === $this->left) {
+ return;
+ }
+
+ $this->resetCalculation();
+ $this->left = $sequence;
+ }
+
+ /**
+ * Set the second sequence ($b) and reset any internal caches to indicate that
+ * when calling the calculation methods, we need to recalculate them.
+ *
+ * @param string|array $sequence The sequence to set as the second sequence.
+ */
+ protected function setRightSequence($sequence)
+ {
+ if (!is_array($sequence)) {
+ $sequence = str_split($sequence);
+ }
+ if ($sequence === $this->right) {
+ return;
+ }
+
+ $this->resetCalculation();
+ $this->right = $sequence;
+ $this->generateRightChain();
+ }
+
+ protected function resetCalculation()
+ {
+ $this->matchingBlocks = null;
+ $this->opCodes = null;
+ }
+
+ /**
+ * @return array
+ */
+ public function getLeftSequence()
+ {
+ return $this->left;
+ }
+
+ /**
+ * @return array
+ */
+ public function getRightSequence()
+ {
+ return $this->right;
+ }
+
+ /**
+ * Generate the internal arrays containing the list of junk and non-junk
+ * characters for the second ($b) sequence.
+ */
+ private function generateRightChain()
+ {
+ $length = count($this->right);
+ $this->b2j = [];
+ $popularDict = [];
+
+ foreach ($this->right as $i => $char) {
+ if (isset($this->b2j[$char])) {
+ if ($length >= 200 && count($this->b2j[$char]) * 100 > $length) {
+ $popularDict[$char] = 1;
+ unset($this->b2j[$char]);
+ } else {
+ $this->b2j[$char][] = $i;
+ }
+ } else {
+ $this->b2j[$char] = [$i];
+ }
+ }
+
+ // Remove leftovers
+ foreach (array_keys($popularDict) as $char) {
+ unset($this->b2j[$char]);
+ }
+
+ $this->junkCharacters = [];
+ if (is_callable($this->junkCallback)) {
+ foreach (array_keys($popularDict) as $char) {
+ if (call_user_func($this->junkCallback, $char)) {
+ $this->junkCharacters[$char] = 1;
+ unset($popularDict[$char]);
+ }
+ }
+
+ foreach (array_keys($this->b2j) as $char) {
+ if (call_user_func($this->junkCallback, $char)) {
+ $this->junkCharacters[$char] = 1;
+ unset($this->b2j[$char]);
+ }
+ }
+ }
+ }
+
+ /**
+ * Checks if a particular character is in the junk dictionary
+ * for the list of junk characters.
+ *
+ * @param $b
+ * @return boolean whether the character is considered junk
+ */
+ private function isBJunk($b)
+ {
+ if (isset($this->junkCharacters[$b])) {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Find the longest matching block in the two sequences, as defined by the
+ * lower and upper constraints for each sequence. (for the first sequence,
+ * $alo - $ahi and for the second sequence, $blo - $bhi)
+ *
+ * Essentially, of all of the maximal matching blocks, return the one that
+ * starts earliest in $a, and all of those maximal matching blocks that
+ * start earliest in $a, return the one that starts earliest in $b.
+ *
+ * If the junk callback is defined, do the above but with the restriction
+ * that the junk element appears in the block. Extend it as far as possible
+ * by matching only junk elements in both $a and $b.
+ *
+ * @param int $beginLeft The lower constraint for the first sequence.
+ * @param int $endLeft The upper constraint for the first sequence.
+ * @param int $beginRight The lower constraint for the second sequence.
+ * @param int $endRight The upper constraint for the second sequence.
+ * @return array Array containing the longest match that includes the starting
+ * position in $a, start in $b and the length/size.
+ */
+ public function findLongestMatch($beginLeft, $endLeft, $beginRight, $endRight)
+ {
+ $left = $this->left;
+ $right = $this->right;
+
+ $bestBeginLeft = $beginLeft;
+ $bestBeginRight = $beginRight;
+ $bestSize = 0;
+
+ $j2Len = [];
+ $nothing = [];
+
+ for ($currentLeft = $beginLeft; $currentLeft < $endLeft; ++$currentLeft) {
+ $newJ2Len = [];
+ $junkList = ArrayHelper::getPropertyOrDefault($this->b2j, $left[$currentLeft], $nothing);
+ foreach ($junkList as $junk) {
+ if ($junk < $beginRight) {
+ continue;
+ }
+ if ($junk >= $endRight) {
+ break;
+ }
+
+ $k = ArrayHelper::getPropertyOrDefault($j2Len, $junk -1, 0) + 1;
+ $newJ2Len[$junk] = $k;
+ if ($k > $bestSize) {
+ $bestBeginLeft = $currentLeft - $k + 1;
+ $bestBeginRight = $junk - $k + 1;
+ $bestSize = $k;
+ }
+ }
+
+ $j2Len = $newJ2Len;
+ }
+
+ while ($bestBeginLeft > $beginLeft
+ && $bestBeginRight > $beginRight
+ && !$this->isBJunk($right[$bestBeginRight - 1])
+ && !$this->linesAreDifferent($bestBeginLeft - 1, $bestBeginRight - 1)
+ ) {
+ --$bestBeginLeft;
+ --$bestBeginRight;
+ ++$bestSize;
+ }
+
+ while ($bestBeginLeft + $bestSize < $endLeft && ($bestBeginRight + $bestSize) < $endRight
+ && !$this->isBJunk($right[$bestBeginRight + $bestSize])
+ && !$this->linesAreDifferent($bestBeginLeft + $bestSize, $bestBeginRight + $bestSize)
+ ) {
+ ++$bestSize;
+ }
+
+ while ($bestBeginLeft > $beginLeft
+ && $bestBeginRight > $beginRight
+ && $this->isBJunk($right[$bestBeginRight - 1])
+ && !$this->linesAreDifferent($bestBeginLeft - 1, $bestBeginRight - 1)
+ ) {
+ --$bestBeginLeft;
+ --$bestBeginRight;
+ ++$bestSize;
+ }
+
+ while ($bestBeginLeft + $bestSize < $endLeft
+ && $bestBeginRight + $bestSize < $endRight
+ && $this->isBJunk($right[$bestBeginRight + $bestSize])
+ && !$this->linesAreDifferent($bestBeginLeft + $bestSize, $bestBeginRight + $bestSize)
+ ) {
+ ++$bestSize;
+ }
+
+ return [$bestBeginLeft, $bestBeginRight, $bestSize];
+ }
+
+ /**
+ * Check if the two lines at the given indexes are different or not.
+ *
+ * @param int $leftIndex Line number to check against in a.
+ * @param int $rightIndex Line number to check against in b.
+ * @return boolean True if the lines are different and false if not.
+ */
+ public function linesAreDifferent($leftIndex, $rightIndex)
+ {
+ $leftLine = $this->left[$leftIndex];
+ $rightLine = $this->right[$rightIndex];
+
+ if ($this->options['ignoreWhitespace']) {
+ $replace = ["\t", ' '];
+ $leftLine = str_replace($replace, '', $leftLine);
+ $rightLine = str_replace($replace, '', $rightLine);
+ }
+
+ if ($this->options['ignoreCase']) {
+ $leftLine = strtolower($leftLine);
+ $rightLine = strtolower($rightLine);
+ }
+
+ return $leftLine !== $rightLine;
+ }
+
+ /**
+ * Return a nested set of arrays for all of the matching sub-sequences
+ * in the strings $a and $b.
+ *
+ * Each block contains the lower constraint of the block in $a, the lower
+ * constraint of the block in $b and finally the number of lines that the
+ * block continues for.
+ *
+ * @return array Nested array of the matching blocks, as described by the function.
+ */
+ public function getMatchingBlocks()
+ {
+ if ($this->matchingBlocks === null) {
+ $this->matchingBlocks = $this->calculateMatchingBlocks();
+ }
+
+ return $this->matchingBlocks;
+ }
+
+ public function calculateMatchingBlocks()
+ {
+ $leftLength = count($this->left);
+ $rightLength = count($this->right);
+
+ $queue = [
+ [0, $leftLength, 0, $rightLength]
+ ];
+
+ $matchingBlocks = [];
+ while (!empty($queue)) {
+ list($leftBegin, $leftEnd, $rightBegin, $rightEnd) = array_pop($queue);
+ $block = $this->findLongestMatch($leftBegin, $leftEnd, $rightBegin, $rightEnd);
+ list($bestBeginLeft, $bestBeginRight, $bestSize) = $block;
+ if ($bestSize) {
+ $matchingBlocks[] = $block;
+ if ($leftBegin < $bestBeginLeft && $rightBegin < $bestBeginRight) {
+ $queue[] = [
+ $leftBegin,
+ $bestBeginLeft,
+ $rightBegin,
+ $bestBeginRight
+ ];
+ }
+
+ if ($bestBeginLeft + $bestSize < $leftEnd && $bestBeginRight + $bestSize < $rightEnd) {
+ $queue[] = [
+ $bestBeginLeft + $bestSize,
+ $leftEnd,
+ $bestBeginRight + $bestSize,
+ $rightEnd
+ ];
+ }
+ }
+ }
+
+ usort($matchingBlocks, [ArrayHelper::class, 'tupleSort']);
+
+ return static::getNonAdjacentBlocks($matchingBlocks, $leftLength, $rightLength);
+ }
+
+ public function getOpcodes()
+ {
+ if ($this->opCodes === null) {
+ $this->opCodes = OpCodeHelper::calculateOpCodes($this->getMatchingBlocks());
+ }
+
+ return $this->opCodes;
+ }
+
+ /**
+ * @param array $matchingBlocks
+ * @param $leftLength
+ * @param $rightLength
+ * @return array
+ */
+ protected static function getNonAdjacentBlocks(array $matchingBlocks, $leftLength, $rightLength)
+ {
+ $newLeft = 0;
+ $newRight = 0;
+ $newCnt = 0;
+ $nonAdjacent = [];
+ foreach ($matchingBlocks as list($beginLeft, $beginRight, $cntLines)) {
+ if ($newLeft + $newCnt === $beginLeft && $newRight + $newCnt === $beginRight) {
+ $newCnt += $cntLines;
+ } else {
+ if ($newCnt) {
+ $nonAdjacent[] = [$newLeft, $newRight, $newCnt];
+ }
+
+ $newLeft = $beginLeft;
+ $newRight = $beginRight;
+ $newCnt = $cntLines;
+ }
+ }
+
+ if ($newCnt) {
+ $nonAdjacent[] = [$newLeft, $newRight, $newCnt];
+ }
+
+ $nonAdjacent[] = [$leftLength, $rightLength, 0];
+ return $nonAdjacent;
+ }
+}
diff --git a/vendor/gipfl/format/composer.json b/vendor/gipfl/format/composer.json
new file mode 100644
index 0000000..10c3b90
--- /dev/null
+++ b/vendor/gipfl/format/composer.json
@@ -0,0 +1,28 @@
+{
+ "name": "gipfl/format",
+ "description": "Arbitrary collection of Format helpers",
+ "type": "library",
+ "license": "MIT",
+ "authors": [
+ {
+ "name": "Thomas Gelf",
+ "email": "thomas@gelf.net"
+ }
+ ],
+ "config": {
+ "sort-packages": true
+ },
+ "autoload": {
+ "psr-4": {
+ "gipfl\\Format\\": "src"
+ }
+ },
+ "require": {
+ "php": ">=5.6.0",
+ "ext-intl": "*"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^9.3 || ^7.5 || ^6.5 || ^5.7",
+ "squizlabs/php_codesniffer": "^3.6"
+ }
+}
diff --git a/vendor/gipfl/format/src/LocalDateFormat.php b/vendor/gipfl/format/src/LocalDateFormat.php
new file mode 100644
index 0000000..60a29e4
--- /dev/null
+++ b/vendor/gipfl/format/src/LocalDateFormat.php
@@ -0,0 +1,41 @@
+<?php
+
+namespace gipfl\Format;
+
+use IntlDateFormatter;
+
+class LocalDateFormat
+{
+ use LocaleAwareness;
+
+ /** @var IntlDateFormatter */
+ protected $formatter;
+
+ /**
+ * @param $time
+ * @return string
+ */
+ public function getFullDay($time)
+ {
+ return $this->formatter()->format($time);
+ }
+
+ protected function formatter()
+ {
+ if ($this->formatter === null) {
+ $this->formatter = new IntlDateFormatter(
+ $this->getLocale(),
+ IntlDateFormatter::FULL,
+ IntlDateFormatter::NONE
+ );
+ $this->formatter->setTimeZone($this->getTimezone());
+ }
+
+ return $this->formatter;
+ }
+
+ protected function reset()
+ {
+ $this->formatter = null;
+ }
+}
diff --git a/vendor/gipfl/format/src/LocalTimeFormat.php b/vendor/gipfl/format/src/LocalTimeFormat.php
new file mode 100644
index 0000000..1ea3ce7
--- /dev/null
+++ b/vendor/gipfl/format/src/LocalTimeFormat.php
@@ -0,0 +1,184 @@
+<?php
+
+namespace gipfl\Format;
+
+use IntlDateFormatter;
+use RuntimeException;
+
+class LocalTimeFormat
+{
+ use LocaleAwareness;
+
+ /** @var IntlDateFormatter */
+ protected $formatter;
+
+ /**
+ * For available symbols please see:
+ * https://unicode-org.github.io/icu/userguide/format_parse/datetime/#date-field-symbol-table
+ *
+ * @param int|float $time Hint: also supports DateTime, DateTimeInterface since 7.1.5
+ * @return string
+ */
+ public function format($time, $pattern)
+ {
+ $result = $this->formatter($pattern)->format($time);
+ if ($result === false) {
+ throw new RuntimeException(sprintf(
+ 'Failed to format %s as "%s": %s (%d)',
+ $time,
+ $pattern,
+ $this->formatter->getErrorMessage(),
+ $this->formatter->getErrorCode()
+ ));
+ }
+
+ return $result;
+ }
+
+ /**
+ * @param $time
+ * @return string
+ */
+ public function getWeekOfYear($time)
+ {
+ return $this->format($time, 'ww');
+ }
+
+ /**
+ * @param $time
+ * @return int
+ */
+ public function getNumericWeekOfYear($time)
+ {
+ return (int) $this->format($time, 'w');
+ }
+
+ /**
+ * @param $time
+ * @return string
+ */
+ public function getDayInMonth($time)
+ {
+ return $this->format($time, 'dd');
+ }
+
+ /**
+ * @param $time
+ * @return int
+ */
+ public function getNumericDayInMonth($time)
+ {
+ return (int) $this->format($time, 'd');
+ }
+
+ /**
+ * @param $time
+ * @return string
+ */
+ public function getWeekdayName($time)
+ {
+ return $this->format($time, 'cccc');
+ }
+
+ /**
+ * @param $time
+ * @return string
+ */
+ public function getShortWeekdayName($time)
+ {
+ return $this->format($time, 'ccc');
+ }
+
+ /**
+ * e.g. September
+ *
+ * @param $time
+ * @return string
+ */
+ public function getMonthName($time)
+ {
+ return $this->format($time, 'LLLL');
+ }
+
+ /**
+ * e.g. Sep
+ *
+ * @param $time
+ * @return string
+ */
+ public function getShortMonthName($time)
+ {
+ return $this->format($time, 'LLL');
+ }
+
+ /**
+ * e.g. 2021
+ * @param $time
+ * @return string
+ */
+ public function getYear($time)
+ {
+ return $this->format($time, 'y');
+ }
+
+ /**
+ * e.g. 21
+ *
+ * @param $time
+ * @return string
+ */
+ public function getShortYear($time)
+ {
+ return $this->format($time, 'yy');
+ }
+
+ /**
+ * e.g. 21:50:12
+ *
+ * @param $time
+ * @return string
+ */
+ public function getTime($time)
+ {
+ if ($this->wantsAmPm()) {
+ return $this->format($time, 'h:mm:ss a');
+ }
+
+ return $this->format($time, 'H:mm:ss');
+ }
+
+ /**
+ * e.g. 21:50
+ *
+ * @param $time
+ * @return string
+ */
+ public function getShortTime($time)
+ {
+ if ($this->wantsAmPm()) {
+ return $this->format($time, 'K:mm a');
+ }
+
+ return $this->format($time, 'H:mm');
+ }
+
+ protected function formatter($pattern)
+ {
+ if ($this->formatter === null) {
+ $this->formatter = new IntlDateFormatter(
+ $this->getLocale(),
+ IntlDateFormatter::GREGORIAN,
+ IntlDateFormatter::GREGORIAN
+ );
+ $this->formatter->setTimeZone($this->getTimezone());
+ }
+ $this->formatter->setPattern($pattern);
+
+ return $this->formatter;
+ }
+
+ protected function reset()
+ {
+ $this->formatter = null;
+ }
+}
diff --git a/vendor/gipfl/format/src/LocaleAwareness.php b/vendor/gipfl/format/src/LocaleAwareness.php
new file mode 100644
index 0000000..cb1a417
--- /dev/null
+++ b/vendor/gipfl/format/src/LocaleAwareness.php
@@ -0,0 +1,94 @@
+<?php
+
+namespace gipfl\Format;
+
+use DateTimeZone;
+use IntlTimeZone;
+use RuntimeException;
+
+trait LocaleAwareness
+{
+ /** @var string */
+ protected $locale;
+
+ /** @var DateTimeZone|IntlTimeZone */
+ protected $timezone;
+
+ /**
+ * @param string $locale
+ * @return void
+ */
+ public function setLocale($locale)
+ {
+ if ($this->locale !== $locale) {
+ $this->locale = (string) $locale;
+ $this->reset();
+ }
+ }
+
+ /**
+ * @param DateTimeZone|IntlTimeZone $timezone
+ * @return void
+ */
+ public function setTimezone($timezone)
+ {
+ // Hint: type checking is delegated to timeZonesAreEqual
+ if (self::timeZonesAreEqual($this->timezone, $timezone)) {
+ return;
+ }
+
+ $this->timezone = $timezone;
+ $this->reset();
+ }
+
+ protected function wantsAmPm()
+ {
+ // TODO: complete this list
+ return in_array($this->getLocale(), ['en_US', 'en_US.UTF-8']);
+ }
+
+ protected function isUsEnglish()
+ {
+ return in_array($this->getLocale(), ['en_US', 'en_US.UTF-8']);
+ }
+
+ protected function getLocale()
+ {
+ if ($this->locale === null) {
+ $this->locale = setlocale(LC_TIME, 0) ?: 'C';
+ }
+
+ return $this->locale;
+ }
+
+ protected function getTimezone()
+ {
+ if ($this->timezone === null) {
+ $this->timezone = new DateTimeZone(date_default_timezone_get());
+ }
+
+ return $this->timezone;
+ }
+
+ protected static function timeZonesAreEqual($left, $right)
+ {
+ if ($left === null && $right !== null) {
+ return false;
+ }
+ if ($left !== null && $right === null) {
+ return false;
+ }
+ if ($left instanceof DateTimeZone) {
+ return $right instanceof DateTimeZone && $left->getName() === $right->getName();
+ }
+ if ($left instanceof IntlTimeZone) {
+ return $right instanceof IntlTimeZone && $left->getID() === $right->getID();
+ }
+
+ throw new RuntimeException(sprintf(
+ 'Valid timezone expected, got left=%s, right=%s',
+ is_object($left) ? get_class($left) : gettype($left),
+ is_object($right) ? get_class($right) : gettype($right)
+ ));
+ }
+}
diff --git a/vendor/gipfl/icinga-cli-daemon/composer.json b/vendor/gipfl/icinga-cli-daemon/composer.json
new file mode 100644
index 0000000..ec38ba2
--- /dev/null
+++ b/vendor/gipfl/icinga-cli-daemon/composer.json
@@ -0,0 +1,24 @@
+{
+ "name": "gipfl/icinga-cli-daemon",
+ "type": "library",
+ "description": "Helpers for Icinga CLI Daemons",
+ "homepage": "https://github.com/gipfl/icinga-cli-daemon",
+ "config": {
+ "sort-packages": true,
+ "platform": {
+ "php": "5.6.3"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "gipfl\\IcingaCliDaemon\\": "src/"
+ }
+ },
+ "require": {
+ "php": ">=5.6.3",
+ "ext-posix": "*",
+ "gipfl/cli": ">=0.5.0",
+ "react/event-loop": ">=1.1",
+ "react/promise": ">=2.7"
+ }
+}
diff --git a/vendor/gipfl/icinga-cli-daemon/src/DbResourceConfigWatch.php b/vendor/gipfl/icinga-cli-daemon/src/DbResourceConfigWatch.php
new file mode 100644
index 0000000..08b188f
--- /dev/null
+++ b/vendor/gipfl/icinga-cli-daemon/src/DbResourceConfigWatch.php
@@ -0,0 +1,163 @@
+<?php
+
+namespace gipfl\IcingaCliDaemon;
+
+use Icinga\Application\Config;
+use InvalidArgumentException;
+use React\EventLoop\LoopInterface;
+
+/**
+ * DbResourceConfigWatch
+ *
+ * Checks every $interval = 3 seconds for changed DB resource configuration.
+ * Notifies registered callbacksin case this happens.
+ */
+class DbResourceConfigWatch
+{
+ /** @var string */
+ protected $configFile;
+
+ /** @var string */
+ protected $resourceConfigFile;
+
+ /** @var string|null */
+ protected $dbResourceName;
+
+ /** @var array|null|false It's false on initialization to trigger */
+ protected $resourceConfig = false;
+
+ /** @var int|float */
+ protected $interval = 3;
+
+ /** @var callable[] */
+ protected $callbacks = [];
+
+ /**
+ * @param string $dbResourceName
+ * @return DbResourceConfigWatch
+ */
+ public static function name($dbResourceName)
+ {
+ $self = new static();
+ $self->dbResourceName = $dbResourceName;
+
+ return $self;
+ }
+
+ /**
+ * @param string $moduleName
+ * @return DbResourceConfigWatch
+ */
+ public static function module($moduleName)
+ {
+ $self = new static();
+ $self->configFile = Config::module($moduleName)->getConfigFile();
+ $self->resourceConfigFile = Config::app('resources')->getConfigFile();
+
+ return $self;
+ }
+
+ /**
+ * @param int|float $interval
+ * @return $this
+ */
+ public function setInterval($interval)
+ {
+ if (! \is_int($interval) && ! \is_float($interval)) {
+ throw new InvalidArgumentException(
+ '$interval needs to be either int or float'
+ );
+ }
+ $this->interval = $interval;
+
+ return $this;
+ }
+
+ /**
+ * @param callable $callable
+ * @return $this
+ */
+ public function notify($callable)
+ {
+ if (! \is_callable($callable)) {
+ throw new InvalidArgumentException('$callable needs to be callable');
+ }
+ $this->callbacks[] = $callable;
+
+ return $this;
+ }
+
+ /**
+ * @param LoopInterface $loop
+ */
+ public function run(LoopInterface $loop)
+ {
+ $check = function () {
+ $this->checkForFreshConfig();
+ };
+ $loop->addPeriodicTimer($this->interval, $check);
+ $loop->futureTick($check);
+ }
+
+ protected function checkForFreshConfig()
+ {
+ if ($this->configHasBeenChanged()) {
+ $this->emitNewConfig($this->resourceConfig);
+ }
+ }
+
+ protected function emitNewConfig($config)
+ {
+ foreach ($this->callbacks as $callback) {
+ $callback($config);
+ }
+ }
+
+ protected function getResourceName()
+ {
+ if ($this->dbResourceName) {
+ return $this->dbResourceName;
+ } else {
+ return $this->loadDbResourceName();
+ }
+ }
+
+ protected function loadDbResourceName()
+ {
+ $parsed = @\parse_ini_file($this->configFile, true);
+ if (isset($parsed['db']['resource'])) {
+ return $parsed['db']['resource'];
+ } else {
+ return null;
+ }
+ }
+
+ protected function loadDbConfigFromDisk($name)
+ {
+ if ($name === null) {
+ return null;
+ }
+
+ $parsed = @\parse_ini_file($this->resourceConfigFile, true);
+ if (isset($parsed[$name])) {
+ $section = $parsed[$name];
+ \ksort($section);
+
+ return $section;
+ } else {
+ return null;
+ }
+ }
+
+ protected function configHasBeenChanged()
+ {
+ $resource = $this->loadDbConfigFromDisk($this->loadDbResourceName());
+ if ($resource !== $this->resourceConfig) {
+ $this->resourceConfig = $resource;
+
+ return true;
+ } else {
+ return false;
+ }
+ }
+}
diff --git a/vendor/gipfl/icinga-cli-daemon/src/FinishedProcessState.php b/vendor/gipfl/icinga-cli-daemon/src/FinishedProcessState.php
new file mode 100644
index 0000000..9ca5e5f
--- /dev/null
+++ b/vendor/gipfl/icinga-cli-daemon/src/FinishedProcessState.php
@@ -0,0 +1,66 @@
+<?php
+
+namespace gipfl\IcingaCliDaemon;
+
+class FinishedProcessState
+{
+ /** @var int|null */
+ protected $exitCode;
+
+ /** @var int|null */
+ protected $termSignal;
+
+ public function __construct($exitCode, $termSignal)
+ {
+ $this->exitCode = $exitCode;
+ $this->termSignal = $termSignal;
+ }
+
+ public function succeeded()
+ {
+ return $this->exitCode === 0;
+ }
+
+ /**
+ * @return int|null
+ */
+ public function getExitCode()
+ {
+ return $this->exitCode;
+ }
+
+ public function getTermSignal()
+ {
+ return $this->termSignal;
+ }
+
+ public function getCombinedExitCode()
+ {
+ if ($this->exitCode === null) {
+ if ($this->termSignal === null) {
+ return 255;
+ } else {
+ return 255 + $this->termSignal;
+ }
+ } else {
+ return $this->exitCode;
+ }
+ }
+
+ public function getReason()
+ {
+ if ($this->exitCode === null) {
+ if ($this->termSignal === null) {
+ return 'Process died';
+ } else {
+ return 'Process got terminated with SIGNAL ' . $this->termSignal;
+ }
+ } else {
+ if ($this->exitCode === 0) {
+ return 'Process finished successfully';
+ } else {
+ return 'Process exited with exit code ' . $this->exitCode;
+ }
+ }
+ }
+}
diff --git a/vendor/gipfl/icinga-cli-daemon/src/IcingaCli.php b/vendor/gipfl/icinga-cli-daemon/src/IcingaCli.php
new file mode 100644
index 0000000..8aad4c6
--- /dev/null
+++ b/vendor/gipfl/icinga-cli-daemon/src/IcingaCli.php
@@ -0,0 +1,144 @@
+<?php
+
+namespace gipfl\IcingaCliDaemon;
+
+use Exception;
+use Evenement\EventEmitterTrait;
+use gipfl\Protocol\JsonRpc\Connection;
+use React\EventLoop\LoopInterface;
+use React\Promise\Deferred;
+use React\Promise\Stream;
+
+class IcingaCli
+{
+ use EventEmitterTrait;
+
+ /** @var IcingaCliRunner */
+ protected $runner;
+
+ /** @var Connection|null */
+ protected $rpc;
+
+ protected $arguments = [];
+
+ /** @var \React\Stream\WritableStreamInterface|null */
+ protected $stdin;
+
+ /** @var Deferred|null */
+ protected $deferredStdin;
+
+ /** @var \React\Stream\ReadableStreamInterface|null */
+ protected $stdout;
+
+ /** @var Deferred|null */
+ protected $deferredStdout;
+
+ /** @var \React\Stream\ReadableStreamInterface|null */
+ protected $stderr;
+
+ /** @var Deferred|null */
+ protected $deferredStderr;
+
+ public function __construct(IcingaCliRunner $runner = null)
+ {
+ if ($runner === null) {
+ $runner = new IcingaCliRunner();
+ }
+ $this->runner = $runner;
+ $this->init();
+ }
+
+ protected function init()
+ {
+ // Override this if you want.
+ }
+
+ public function setArguments($arguments)
+ {
+ $this->arguments = $arguments;
+
+ return $this;
+ }
+
+ public function getArguments()
+ {
+ return $this->arguments;
+ }
+
+ public function run(LoopInterface $loop)
+ {
+ $process = $this->runner->command($this->getArguments());
+ $canceller = function () use ($process) {
+ // TODO: first soft, then hard
+ $process->terminate();
+ };
+ $deferred = new Deferred($canceller);
+ $process->on('exit', function ($exitCode, $termSignal) use ($deferred) {
+ $state = new FinishedProcessState($exitCode, $termSignal);
+ if ($state->succeeded()) {
+ $deferred->resolve();
+ } else {
+ $deferred->reject(new Exception($state->getReason()));
+ }
+ });
+
+ $process->start($loop);
+ if ($this->deferredStdin instanceof Deferred) {
+ $this->deferredStdin->resolve($process->stdin);
+ } else {
+ $this->stdin = $process->stdin;
+ }
+ if ($this->deferredStdout instanceof Deferred) {
+ $this->deferredStdout->resolve($process->stdout);
+ } else {
+ $this->stdout = $process->stdout;
+ }
+ if ($this->deferredStderr instanceof Deferred) {
+ $this->deferredStderr->resolve($process->stderr);
+ } else {
+ $this->stderr = $process->stderr;
+ }
+ $this->emit('start', [$process]);
+
+ return $deferred->promise();
+ }
+
+ /**
+ * @return \React\Stream\WritableStreamInterface
+ */
+ public function stdin()
+ {
+ if ($this->stdin === null) {
+ $this->deferredStdin = new Deferred();
+ $this->stdin = Stream\unwrapWritable($this->deferredStdin->promise());
+ }
+
+ return $this->stdin;
+ }
+
+ /**
+ * @return \React\Stream\ReadableStreamInterface
+ */
+ public function stdout()
+ {
+ if ($this->stdout === null) {
+ $this->deferredStdout = new Deferred();
+ $this->stdout = Stream\unwrapReadable($this->deferredStdout->promise());
+ }
+
+ return $this->stdout;
+ }
+
+ /**
+ * @return \React\Stream\ReadableStreamInterface
+ */
+ public function stderr()
+ {
+ if ($this->stderr === null) {
+ $this->deferredStderr = new Deferred();
+ $this->stderr = Stream\unwrapReadable($this->deferredStderr->promise());
+ }
+
+ return $this->stderr;
+ }
+}
diff --git a/vendor/gipfl/icinga-cli-daemon/src/IcingaCliRpc.php b/vendor/gipfl/icinga-cli-daemon/src/IcingaCliRpc.php
new file mode 100644
index 0000000..473d4ed
--- /dev/null
+++ b/vendor/gipfl/icinga-cli-daemon/src/IcingaCliRpc.php
@@ -0,0 +1,45 @@
+<?php
+
+namespace gipfl\IcingaCliDaemon;
+
+use Exception;
+use gipfl\Protocol\JsonRpc\Connection;
+use gipfl\Protocol\NetString\StreamWrapper;
+use React\ChildProcess\Process;
+
+class IcingaCliRpc extends IcingaCli
+{
+ /** @var IcingaCliRunner */
+ protected $runner;
+
+ /** @var Connection|null */
+ protected $rpc;
+
+ protected $arguments = [];
+
+ protected function init()
+ {
+ $this->on('start', function (Process $process) {
+ $netString = new StreamWrapper(
+ $process->stdout,
+ $process->stdin
+ );
+ $netString->on('error', function (Exception $e) {
+ $this->emit('error', [$e]);
+ });
+ $this->rpc()->handle($netString);
+ });
+ }
+
+ /**
+ * @return Connection
+ */
+ public function rpc()
+ {
+ if ($this->rpc === null) {
+ $this->rpc = new Connection();
+ }
+
+ return $this->rpc;
+ }
+}
diff --git a/vendor/gipfl/icinga-cli-daemon/src/IcingaCliRunner.php b/vendor/gipfl/icinga-cli-daemon/src/IcingaCliRunner.php
new file mode 100644
index 0000000..60a25cc
--- /dev/null
+++ b/vendor/gipfl/icinga-cli-daemon/src/IcingaCliRunner.php
@@ -0,0 +1,88 @@
+<?php
+
+namespace gipfl\IcingaCliDaemon;
+
+use React\ChildProcess\Process;
+use gipfl\Cli\Process as CliProcess;
+
+class IcingaCliRunner
+{
+ /** @var string */
+ protected $binary;
+
+ /** @var string|null */
+ protected $cwd;
+
+ /** @var array|null */
+ protected $env;
+
+ public function __construct($binary = null)
+ {
+ if ($binary === null) {
+ $this->binary = CliProcess::getBinaryPath();
+ $this->cwd = CliProcess::getInitialCwd();
+ } else {
+ $this->binary = $binary;
+ }
+ }
+
+ /**
+ * @param mixed array|...$arguments
+ * @return Process
+ */
+ public function command($arguments = null)
+ {
+ if (! \is_array($arguments)) {
+ $arguments = \func_get_args();
+ }
+
+ return new Process(
+ $this->escapedCommand($arguments),
+ $this->cwd,
+ $this->env
+ );
+ }
+
+ /**
+ * @param string|null $cwd
+ */
+ public function setCwd($cwd)
+ {
+ if ($cwd === null) {
+ $this->cwd = $cwd;
+ } else {
+ $this->cwd = (string) $cwd;
+ }
+ }
+
+ /**
+ * @param array|null $env
+ */
+ public function setEnv($env)
+ {
+ if ($env === null) {
+ $this->env = $env;
+ } else {
+ $this->env = (array) $env;
+ }
+ }
+
+ /**
+ * @param $arguments
+ * @return string
+ */
+ protected function escapedCommand($arguments)
+ {
+ $command = ['exec', \escapeshellcmd($this->binary)];
+
+ foreach ($arguments as $argument) {
+ if (\ctype_alnum(preg_replace('/^-{1,2}/', '', $argument))) {
+ $command[] = $argument;
+ } else {
+ $command[] = \escapeshellarg($argument);
+ }
+ }
+
+ return \implode(' ', $command);
+ }
+}
diff --git a/vendor/gipfl/icinga-cli-daemon/src/RetryUnless.php b/vendor/gipfl/icinga-cli-daemon/src/RetryUnless.php
new file mode 100644
index 0000000..0303b2c
--- /dev/null
+++ b/vendor/gipfl/icinga-cli-daemon/src/RetryUnless.php
@@ -0,0 +1,244 @@
+<?php
+
+namespace gipfl\IcingaCliDaemon;
+
+use Exception;
+use Icinga\Application\Logger;
+use React\EventLoop\LoopInterface;
+use React\EventLoop\TimerInterface;
+use React\Promise\Deferred;
+use React\Promise\PromiseInterface;
+use RuntimeException;
+
+class RetryUnless
+{
+ /** @var LoopInterface */
+ protected $loop;
+
+ /** @var Deferred */
+ protected $deferred;
+
+ /** @var TimerInterface */
+ protected $timer;
+
+ /** @var callable */
+ protected $callback;
+
+ /** @var bool */
+ protected $expectsSuccess;
+
+ /** @var int Regular interval */
+ protected $interval = 1;
+
+ /** @var int|null Optional, interval will be changed after $burst attempts */
+ protected $burst = null;
+
+ /** @var int|null Interval after $burst attempts */
+ protected $finalInterval = null;
+
+ /** @var int Current attempt count */
+ protected $attempts = 0;
+
+ /** @var bool No attempts will be made while paused */
+ protected $paused = false;
+
+ protected $lastError;
+
+ protected function __construct($callback, $expectsSuccess = true)
+ {
+ $this->callback = $callback;
+ $this->expectsSuccess = $expectsSuccess;
+ }
+
+ public static function succeeding($callback)
+ {
+ return new static($callback);
+ }
+
+ public static function failing($callback)
+ {
+ return new static($callback, false);
+ }
+
+ public function run(LoopInterface $loop)
+ {
+ $this->assertNotRunning();
+ $this->deferred = $deferred = new Deferred();
+ $this->loop = $loop;
+ $loop->futureTick(function () {
+ $this->nextAttempt();
+ });
+
+ return $deferred->promise();
+ }
+
+ public function getLastError()
+ {
+ return $this->lastError;
+ }
+
+ public function setInterval($interval)
+ {
+ $this->interval = $interval;
+
+ return $this;
+ }
+
+ public function slowDownAfter($burst, $interval)
+ {
+ $this->burst = $burst;
+ $this->finalInterval = $interval;
+
+ return $this;
+ }
+
+ public function pause()
+ {
+ $this->removeEventualTimer();
+ $this->paused = true;
+
+ return $this;
+ }
+
+ public function resume()
+ {
+ if ($this->paused) {
+ $this->paused = false;
+ if ($this->timer === null) {
+ $this->nextAttempt();
+ }
+ }
+ }
+
+ public function reset()
+ {
+ $this->attempts = 0;
+ $this->paused = false;
+ $this->removeEventualTimer();
+ $this->rejectEventualDeferred('RetryUnless has been reset');
+
+ return $this;
+ }
+
+ public function getAttempts()
+ {
+ return $this->attempts;
+ }
+
+ protected function nextAttempt()
+ {
+ if ($this->paused) {
+ return;
+ }
+
+ $this->removeEventualTimer();
+ $this->attempts++;
+ try {
+ $callback = $this->callback;
+ $this->handleResult($callback());
+ } catch (Exception $e) {
+ $this->handleResult($e);
+ }
+ }
+
+ protected function logError(Exception $e)
+ {
+ if ($this->lastError !== $e->getMessage()) {
+ $this->lastError = $e->getMessage();
+ Logger::error($e);
+ }
+ }
+
+ protected function handleResult($result)
+ {
+ if ($this->expectsSuccess) {
+ if ($result instanceof Exception) {
+ $this->logError($result);
+ $this->scheduleNextAttempt();
+ } elseif ($result instanceof PromiseInterface) {
+ $later = function ($result) {
+ $this->handleResult($result);
+ };
+ $result->then($later, $later);
+ } else {
+ $this->succeed($result);
+ }
+ } else {
+ if ($result instanceof Exception) {
+ $this->succeed($result);
+ } else {
+ $this->scheduleNextAttempt();
+ }
+ }
+ }
+
+ protected function scheduleNextAttempt()
+ {
+ if ($this->timer !== null) {
+ throw new RuntimeException(
+ 'RetryUnless schedules next attempt while already scheduled'
+ );
+ }
+ $this->timer = $this->loop->addTimer($this->getNextInterval(), function () {
+ $this->nextAttempt();
+ });
+ }
+
+ protected function succeed($result)
+ {
+ $this->removeEventualTimer();
+ if ($this->deferred === null) {
+ Logger::warning('RetryUnless tries to resolve twice');
+
+ return;
+ }
+ $this->deferred->resolve($result);
+ $this->deferred = null;
+ $this->reset();
+ }
+
+ protected function getNextInterval()
+ {
+ if ($this->burst === null) {
+ return $this->interval;
+ }
+
+ return $this->attempts >= $this->burst
+ ? $this->finalInterval
+ : $this->interval;
+ }
+
+ protected function assertNotRunning()
+ {
+ if ($this->deferred) {
+ throw new RuntimeException(
+ 'Cannot re-run RetryUnless while already running'
+ );
+ }
+ }
+
+ protected function removeEventualTimer()
+ {
+ if ($this->timer) {
+ $this->loop->cancelTimer($this->timer);
+ $this->timer = null;
+ }
+ }
+
+ protected function rejectEventualDeferred($reason)
+ {
+ if ($this->deferred !== null) {
+ $deferred = $this->deferred;
+ $this->deferred = null;
+ $deferred->reject($reason);
+ }
+ }
+
+ public function __destruct()
+ {
+ $this->removeEventualTimer();
+ $this->rejectEventualDeferred('RetryUnless has been destructed');
+
+ $this->loop = null;
+ }
+}
diff --git a/vendor/gipfl/icinga-cli-daemon/src/StateMachine.php b/vendor/gipfl/icinga-cli-daemon/src/StateMachine.php
new file mode 100644
index 0000000..d8ec470
--- /dev/null
+++ b/vendor/gipfl/icinga-cli-daemon/src/StateMachine.php
@@ -0,0 +1,113 @@
+<?php
+
+namespace gipfl\IcingaCliDaemon;
+
+use RuntimeException;
+
+trait StateMachine
+{
+ /** @var string */
+ private $currentState;
+
+ /** @var array [fromState][toState] = [callback, ...] */
+ private $allowedTransitions = [];
+
+ /** @var array [state] = [callback, ...] */
+ private $onState = [];
+
+ public function initializeStateMachine($initialState)
+ {
+ if ($this->currentState !== null) {
+ throw new RuntimeException('StateMachine has already been initialized');
+ }
+ $this->currentState = $initialState;
+ }
+
+ /**
+ * @param string|array $fromState
+ * @param string $toState
+ * @param callable $callback
+ * @return $this
+ */
+ public function onTransition($fromState, $toState, $callback)
+ {
+ if (is_array($fromState)) {
+ foreach ($fromState as $state) {
+ $this->onTransition($state, $toState, $callback);
+ }
+ } else {
+ $this->allowTransition($fromState, $toState);
+ $this->allowedTransitions[$fromState][$toState][] = $callback;
+ }
+
+ return $this;
+ }
+
+ public function allowTransition($fromState, $toState)
+ {
+ if (! isset($this->allowedTransitions[$fromState][$toState])) {
+ $this->allowedTransitions[$fromState][$toState] = [];
+ }
+
+ return $this;
+ }
+
+ /**
+ * @param $state
+ * @param $callback
+ * @return $this
+ */
+ public function onState($state, $callback)
+ {
+ if (! isset($this->onState[$state])) {
+ $this->onState[$state] = [];
+ }
+
+ $this->onState[$state][] = $callback;
+
+ return $this;
+ }
+
+ public function getState()
+ {
+ if ($this->currentState === null) {
+ throw new RuntimeException('StateMachine has not been initialized');
+ }
+
+ return $this->currentState;
+ }
+
+ public function setState($state)
+ {
+ $fromState = $this->getState();
+ if ($this->canTransit($fromState, $state)) {
+ $this->currentState = $state;
+ $this->runStateTransition($fromState, $state);
+ } else {
+ throw new RuntimeException(sprintf(
+ 'A transition from %s to %s is not allowed',
+ $fromState,
+ $state
+ ));
+ }
+ }
+
+ private function runStateTransition($fromState, $toState)
+ {
+ if (isset($this->allowedTransitions[$fromState][$toState])) {
+ foreach ($this->allowedTransitions[$fromState][$toState] as $callback) {
+ $callback();
+ }
+ }
+ if (isset($this->onState[$toState])) {
+ foreach ($this->onState[$toState] as $callback) {
+ $callback();
+ }
+ }
+ }
+
+ public function canTransit($fromState, $toState)
+ {
+ return isset($this->allowedTransitions[$fromState][$toState]);
+ }
+}
diff --git a/vendor/gipfl/icingaweb2/composer.json b/vendor/gipfl/icingaweb2/composer.json
new file mode 100644
index 0000000..25a757f
--- /dev/null
+++ b/vendor/gipfl/icingaweb2/composer.json
@@ -0,0 +1,21 @@
+{
+ "name": "gipfl/icingaweb2",
+ "type": "library",
+ "description": "Helpers and glue for Icinga Web 2",
+ "homepage": "https://github.com/gipfl/icingaweb2",
+ "config": {
+ "sort-packages": true
+ },
+ "autoload": {
+ "psr-4": {
+ "gipfl\\IcingaWeb2\\": "src/"
+ }
+ },
+ "require": {
+ "php": ">=5.6",
+ "ext-ctype": "*",
+ "gipfl/format": ">=0.3",
+ "gipfl/translation": ">=0.1",
+ "ipl/html": ">=0.2.1"
+ }
+}
diff --git a/vendor/gipfl/icingaweb2/public/css/11-action-bar.less b/vendor/gipfl/icingaweb2/public/css/11-action-bar.less
new file mode 100644
index 0000000..ab3d737
--- /dev/null
+++ b/vendor/gipfl/icingaweb2/public/css/11-action-bar.less
@@ -0,0 +1,105 @@
+div.gipfl-action-bar {
+ > * {
+ vertical-align: middle;
+ }
+ .pagination-control {
+ float: none;
+ clear: none;
+ display: inline-block;
+ line-height: inherit;
+ margin: 0;
+ }
+
+ form input {
+ margin: 0;
+ }
+}
+
+div.gipfl-action-bar a, div.gipfl-action-bar form i {
+ color: @icinga-blue;
+}
+
+div.gipfl-action-bar a.badge {
+ color: inherit;
+}
+
+div.gipfl-action-bar > a {
+ margin-right: 1em;
+}
+
+#layout.twocols div.gipfl-action-bar .pagination-control {
+ li {
+ display: none;
+ }
+
+ li:nth-child(1), li.active, li:last-child {
+ display: list-item;
+ }
+
+ li.active a {
+ border-bottom: none;
+ }
+}
+
+/** Dropdown in action bar **/
+div.gipfl-action-bar ul {
+ padding: 0;
+ margin: 0;
+
+ li {
+ list-style-type: none;
+ a { display: block; }
+ }
+}
+
+div.gipfl-action-bar > ul {
+ display: inline-block;
+ > li {
+ display: inline-block;
+ }
+ ul {
+ padding: 0.5em 1em 0 0;
+ min-width: 10em;
+ position: absolute;
+ display: none;
+ background-color: @menu-flyout-bg-color;
+ border: 1px solid @gray-light;
+ box-shadow: 0.3em 0.3em 0.3em 0 rgba(0, 0, 0, 0.2);
+
+ a {
+ display: block;
+ padding: 0.3em 0.5em 0.3em 1em;
+ margin: 0;
+ outline: none;
+ color: @menu-flyout-color;
+
+ &:hover {
+ text-decoration: underline;
+ &:before {
+ text-decoration: none;
+ }
+ }
+ }
+
+ li.active a {
+ font-weight: bold;
+ }
+ }
+ > li:hover ul {
+ display: block;
+ z-index: 2;
+ }
+ > li > a {
+ padding: 0.2em 0.5em;
+ }
+ > li:hover {
+ background-color: @tr-active-color;
+ border-top-left-radius: 0.1em;
+ border-top-right-radius: 0.1em;
+ & > a {
+ color: @icinga-blue;
+ text-decoration: none;
+ }
+ }
+}
+/** END of dropdown in action bar **/
diff --git a/vendor/gipfl/icingaweb2/public/css/12-quicksearch.less b/vendor/gipfl/icingaweb2/public/css/12-quicksearch.less
new file mode 100644
index 0000000..a5da7e9
--- /dev/null
+++ b/vendor/gipfl/icingaweb2/public/css/12-quicksearch.less
@@ -0,0 +1,17 @@
+form.gipfl-quicksearch {
+ display: block;
+ input.search {
+ width: 8em;
+ min-width: unset;
+ border: none;
+ background-color: inherit;
+ padding-left: 2em;
+ margin-left: 1.5em;
+ font-size: 0.75em;
+ font-weight: normal;
+ &:focus {
+ width: 16em;
+ border: none;
+ }
+ }
+}
diff --git a/vendor/gipfl/icingaweb2/src/CompatController.php b/vendor/gipfl/icingaweb2/src/CompatController.php
new file mode 100644
index 0000000..952b4b5
--- /dev/null
+++ b/vendor/gipfl/icingaweb2/src/CompatController.php
@@ -0,0 +1,581 @@
+<?php
+
+namespace gipfl\IcingaWeb2;
+
+use gipfl\IcingaWeb2\Controller\Extension\AutoRefreshHelper;
+use gipfl\IcingaWeb2\Controller\Extension\ConfigHelper;
+use gipfl\Translation\StaticTranslator;
+use gipfl\Translation\TranslationHelper;
+use gipfl\IcingaWeb2\Controller\Extension\ControlsAndContentHelper;
+use gipfl\IcingaWeb2\Widget\ControlsAndContent;
+use gipfl\IcingaWeb2\Zf1\SimpleViewRenderer;
+use GuzzleHttp\Psr7\ServerRequest;
+use Icinga\Application\Benchmark;
+use Icinga\Application\Icinga;
+use Icinga\Application\Modules\Manager;
+use Icinga\Application\Modules\Module;
+use Icinga\Authentication\Auth;
+use Icinga\Exception\ProgrammingError;
+use Icinga\File\Pdf;
+use Icinga\Security\SecurityException;
+use Icinga\User;
+use Icinga\Web\Notification;
+use Icinga\Web\Session;
+use Icinga\Web\UrlParams;
+use Icinga\Web\Url as WebUrl;
+use Icinga\Web\Window;
+use InvalidArgumentException;
+use Psr\Http\Message\ServerRequestInterface;
+use RuntimeException;
+use Zend_Controller_Action;
+use Zend_Controller_Action_HelperBroker as ZfHelperBroker;
+use Zend_Controller_Request_Abstract as ZfRequest;
+use Zend_Controller_Response_Abstract as ZfResponse;
+
+/**
+ * Class CompatController
+ * @method \Icinga\Web\Request getRequest() {
+ * {@inheritdoc}
+ * @return \Icinga\Web\Request
+ * }
+ *
+ * @method \Icinga\Web\Response getResponse() {
+ * {@inheritdoc}
+ * @return \Icinga\Web\Response
+ * }
+ */
+class CompatController extends Zend_Controller_Action implements ControlsAndContent
+{
+ use AutoRefreshHelper;
+ use ControlsAndContentHelper;
+ use ConfigHelper;
+ use TranslationHelper;
+
+ /** @var bool Whether the controller requires the user to be authenticated */
+ protected $requiresAuthentication = true;
+
+ protected $applicationName = 'Icinga Web';
+
+ /** @var string The current module's name */
+ private $moduleName;
+
+ private $module;
+
+ private $window;
+
+ // https://github.com/joshbuchea/HEAD
+
+ /** @var SimpleViewRenderer */
+ protected $viewRenderer;
+
+ /** @var bool */
+ private $reloadCss = false;
+
+ /** @var bool */
+ private $rerenderLayout = false;
+
+ /** @var string */
+ private $xhrLayout = 'inline';
+
+ /** @var \Zend_Layout */
+ private $layout;
+
+ /** @var string The inner layout (inside the body) to use */
+ private $innerLayout = 'body';
+
+ /**
+ * Authentication manager
+ *
+ * @var Auth|null
+ */
+ private $auth;
+
+ /** @var UrlParams */
+ protected $params;
+
+ /**
+ * The constructor starts benchmarking, loads the configuration and sets
+ * other useful controller properties
+ *
+ * @param ZfRequest $request
+ * @param ZfResponse $response
+ * @param array $invokeArgs Any additional invocation arguments
+ * @throws SecurityException
+ */
+ public function __construct(
+ ZfRequest $request,
+ ZfResponse $response,
+ array $invokeArgs = array()
+ ) {
+ /** @var \Icinga\Web\Request $request */
+ /** @var \Icinga\Web\Response $response */
+ $this->setRequest($request)
+ ->setResponse($response)
+ ->_setInvokeArgs($invokeArgs);
+
+ $this->prepareViewRenderer();
+ $this->_helper = new ZfHelperBroker($this);
+
+ $this->handlerBrowserWindows();
+ $this->initializeTranslator();
+ $this->initializeLayout();
+
+ $this->view->compact = $request->getParam('view') === 'compact';
+ $url = $this->url();
+ $this->params = $url->getParams();
+
+ if ($url->shift('showCompact')) {
+ $this->view->compact = true;
+ }
+
+ $this->checkPermissionBasics();
+ Benchmark::measure('Ready to initialize the controller');
+ $this->prepareInit();
+ $this->init();
+ }
+
+ protected function initializeLayout()
+ {
+ $url = $this->url();
+ $layout = $this->layout = $this->_helper->layout();
+ $layout->isIframe = $url->shift('isIframe');
+ $layout->showFullscreen = $url->shift('showFullscreen');
+ $layout->moduleName = $this->getModuleName();
+ if ($this->rerenderLayout = $url->shift('renderLayout')) {
+ $this->xhrLayout = $this->innerLayout;
+ }
+ if ($url->shift('_disableLayout')) {
+ $this->layout->disableLayout();
+ }
+ }
+
+ /**
+ * Prepare controller initialization
+ *
+ * As it should not be required for controllers to call the parent's init() method,
+ * base controllers should use prepareInit() in order to prepare the controller
+ * initialization.
+ *
+ * @see \Zend_Controller_Action::init() For the controller initialization method.
+ */
+ protected function prepareInit()
+ {
+ }
+
+ /**
+ * Return the current module's name
+ *
+ * @return string
+ */
+ public function getModuleName()
+ {
+ if ($this->moduleName === null) {
+ $this->moduleName = $this->getRequest()->getModuleName();
+ }
+
+ return $this->moduleName;
+ }
+
+ protected function setModuleName($name)
+ {
+ $this->moduleName = $name;
+
+ return $this;
+ }
+
+ /**
+ * Return this controller's module
+ *
+ * @return Module
+ * @codingStandardsIgnoreStart
+ */
+ public function Module()
+ {
+ // @codingStandardsIgnoreEnd
+ if ($this->module === null) {
+ try {
+ $this->module = Icinga::app()->getModuleManager()->getModule($this->getModuleName());
+ } catch (ProgrammingError $e) {
+ throw new RuntimeException($e->getMessage(), $e->getCode(), $e);
+ }
+ }
+
+ return $this->module;
+ }
+
+ /**
+ * @return Window
+ * @codingStandardsIgnoreStart
+ */
+ public function Window()
+ {
+ // @codingStandardsIgnoreEnd
+ if ($this->window === null) {
+ $this->window = new Window(
+ $this->getRequestHeader('X-Icinga-WindowId', Window::UNDEFINED)
+ );
+ }
+ return $this->window;
+ }
+
+ protected function handlerBrowserWindows()
+ {
+ if ($this->isXhr()) {
+ $id = $this->getRequestHeader('X-Icinga-WindowId', Window::UNDEFINED);
+
+ if ($id === Window::UNDEFINED) {
+ $this->window = new Window($id);
+ $this->_response->setHeader('X-Icinga-WindowId', Window::generateId());
+ }
+ }
+ }
+
+ /**
+ * @return ServerRequestInterface
+ */
+ protected function getServerRequest()
+ {
+ return ServerRequest::fromGlobals();
+ }
+
+ protected function getRequestHeader($key, $default = null)
+ {
+ try {
+ $value = $this->getRequest()->getHeader($key);
+ } catch (\Zend_Controller_Request_Exception $e) {
+ throw new InvalidArgumentException($e->getMessage(), $e->getCode(), $e);
+ }
+
+ if ($value === null) {
+ return $default;
+ } else {
+ return $value;
+ }
+ }
+
+ /**
+ * @throws SecurityException
+ */
+ protected function checkPermissionBasics()
+ {
+ $request = $this->getRequest();
+ // $auth->authenticate($request, $response, $this->requiresLogin());
+ if ($this->requiresLogin()) {
+ if (! $request->isXmlHttpRequest() && $request->isApiRequest()) {
+ Auth::getInstance()->challengeHttp();
+ }
+ $this->redirectToLogin(Url::fromRequest());
+ }
+ if (($this->Auth()->isAuthenticated() || $this->requiresLogin())
+ && $this->getFrontController()->getDefaultModule() !== $this->getModuleName()) {
+ $this->assertPermission(Manager::MODULE_PERMISSION_NS . $this->getModuleName());
+ }
+ }
+
+ protected function initializeTranslator()
+ {
+ $moduleName = $this->getModuleName();
+ $domain = $moduleName !== 'default' ? $moduleName : 'icinga';
+ $this->view->translationDomain = $domain;
+ StaticTranslator::set(new Translator($domain));
+ }
+
+ public function init()
+ {
+ // Hint: we intentionally do not call our parent's init() method
+ }
+
+ /**
+ * Get the authentication manager
+ *
+ * @return Auth
+ * @codingStandardsIgnoreStart
+ */
+ public function Auth()
+ {
+ // @codingStandardsIgnoreEnd
+ if ($this->auth === null) {
+ $this->auth = Auth::getInstance();
+ }
+ return $this->auth;
+ }
+
+ /**
+ * Whether the current user has the given permission
+ *
+ * @param string $permission Name of the permission
+ *
+ * @return bool
+ */
+ public function hasPermission($permission)
+ {
+ return $this->Auth()->hasPermission($permission);
+ }
+
+ /**
+ * Assert that the current user has the given permission
+ *
+ * @param string $permission Name of the permission
+ *
+ * @throws SecurityException If the current user lacks the given permission
+ */
+ public function assertPermission($permission)
+ {
+ if (! $this->Auth()->hasPermission($permission)) {
+ throw new SecurityException('No permission for %s', $permission);
+ }
+ }
+
+ /**
+ * Return restriction information for an eventually authenticated user
+ *
+ * @param string $name Restriction name
+ *
+ * @return array
+ */
+ public function getRestrictions($name)
+ {
+ return $this->Auth()->getRestrictions($name);
+ }
+
+ /**
+ * Check whether the controller requires a login. That is when the controller requires authentication and the
+ * user is currently not authenticated
+ *
+ * @return bool
+ */
+ protected function requiresLogin()
+ {
+ if (! $this->requiresAuthentication) {
+ return false;
+ }
+
+ return ! $this->Auth()->isAuthenticated();
+ }
+
+ public function prepareViewRenderer()
+ {
+ $this->viewRenderer = new SimpleViewRenderer();
+ $this->viewRenderer->replaceZendViewRenderer();
+ $this->view = $this->viewRenderer->view;
+ }
+
+ /**
+ * @return SimpleViewRenderer
+ */
+ public function getViewRenderer()
+ {
+ return $this->viewRenderer;
+ }
+
+ protected function redirectXhr($url)
+ {
+ if (! $url instanceof WebUrl) {
+ $url = Url::fromPath($url);
+ }
+
+ if ($this->rerenderLayout) {
+ $this->getResponse()->setHeader('X-Icinga-Rerender-Layout', 'yes');
+ }
+ if ($this->reloadCss) {
+ $this->getResponse()->setHeader('X-Icinga-Reload-Css', 'now');
+ }
+
+ $this->shutdownSession();
+
+ $this->getResponse()
+ ->setHeader('X-Icinga-Redirect', rawurlencode($url->getAbsoluteUrl()))
+ ->sendHeaders();
+
+ exit;
+ }
+
+ /**
+ * Detect whether the current request requires changes in the layout and apply them before rendering
+ *
+ * @see Zend_Controller_Action::postDispatch()
+ */
+ public function postDispatch()
+ {
+ Benchmark::measure('Action::postDispatch()');
+
+ $layout = $this->layout;
+ $req = $this->getRequest();
+ $layout->innerLayout = $this->innerLayout;
+
+ /** @var User $user */
+ if ($user = $req->getUser()) {
+ if ((bool) $user->getPreferences()->getValue('icingaweb', 'show_benchmark', false)) {
+ if ($layout->isEnabled()) {
+ $layout->benchmark = $this->renderBenchmark();
+ }
+ }
+
+ if (! (bool) $user->getPreferences()->getValue('icingaweb', 'auto_refresh', true)) {
+ $this->disableAutoRefresh();
+ }
+ }
+
+ if ($req->getParam('format') === 'pdf') {
+ $this->shutdownSession();
+ $this->sendAsPdf();
+ return;
+ }
+
+ if ($this->isXhr()) {
+ $this->postDispatchXhr();
+ }
+
+ $this->shutdownSession();
+ }
+
+ public function postDispatchXhr()
+ {
+ $this->layout->setLayout($this->xhrLayout);
+ $resp = $this->getResponse();
+
+ $notifications = Notification::getInstance();
+ if ($notifications->hasMessages()) {
+ $notificationList = array();
+ foreach ($notifications->popMessages() as $m) {
+ $notificationList[] = rawurlencode($m->type . ' ' . $m->message);
+ }
+ $resp->setHeader('X-Icinga-Notification', implode('&', $notificationList), true);
+ }
+
+ if ($this->reloadCss) {
+ $resp->setHeader('X-Icinga-CssReload', 'now', true);
+ }
+
+ if ($this->title) {
+ if (preg_match('~[\r\n]~', $this->title)) {
+ // TODO: Innocent exception and error log for hack attempts
+ throw new InvalidArgumentException('No newlines allowed in page title');
+ }
+ $resp->setHeader(
+ 'X-Icinga-Title',
+ // TODO: Config
+ rawurlencode($this->title . ' :: ' . $this->applicationName),
+ true
+ );
+ } else {
+ // TODO: config
+ $resp->setHeader('X-Icinga-Title', rawurlencode($this->applicationName), true);
+ }
+
+ if ($this->rerenderLayout) {
+ $this->getResponse()->setHeader('X-Icinga-Container', 'layout', true);
+ }
+
+ if (isset($this->autorefreshInterval)) {
+ $resp->setHeader('X-Icinga-Refresh', $this->autorefreshInterval, true);
+ }
+
+ if ($name = $this->getModuleName()) {
+ $this->getResponse()->setHeader('X-Icinga-Module', $name, true);
+ }
+ }
+
+ /**
+ * Redirect to login
+ *
+ * XHR will always redirect to __SELF__ if an URL to redirect to after successful login is set. __SELF__ instructs
+ * JavaScript to redirect to the current window's URL if it's an auto-refresh request or to redirect to the URL
+ * which required login if it's not an auto-refreshing one.
+ *
+ * XHR will respond with HTTP status code 403 Forbidden.
+ *
+ * @param Url|string $redirect URL to redirect to after successful login
+ */
+ protected function redirectToLogin($redirect = null)
+ {
+ $login = Url::fromPath('authentication/login');
+ if ($this->isXhr()) {
+ if ($redirect !== null) {
+ $login->setParam('redirect', '__SELF__');
+ }
+
+ $this->_response->setHttpResponseCode(403);
+ } elseif ($redirect !== null) {
+ if (! $redirect instanceof Url) {
+ $redirect = Url::fromPath($redirect);
+ }
+
+ if (($relativeUrl = $redirect->getRelativeUrl())) {
+ $login->setParam('redirect', $relativeUrl);
+ }
+ }
+
+ $this->rerenderLayout()->redirectNow($login);
+ }
+
+ protected function sendAsPdf()
+ {
+ $pdf = new Pdf();
+ $pdf->renderControllerAction($this);
+ }
+
+ /**
+ * Render the benchmark
+ *
+ * @return string Benchmark HTML
+ */
+ protected function renderBenchmark()
+ {
+ $this->viewRenderer->postDispatch();
+ Benchmark::measure('Response ready');
+ return Benchmark::renderToHtml()/*
+ . '<pre style="height: 16em; vertical-overflow: scroll">'
+ . print_r(get_included_files(), 1)
+ . '</pre>'*/;
+ }
+
+ public function isXhr()
+ {
+ return $this->getRequest()->isXmlHttpRequest();
+ }
+
+ protected function redirectHttp($url)
+ {
+ if (! $url instanceof Url) {
+ $url = Url::fromPath($url);
+ }
+ $this->shutdownSession();
+ $this->_helper->Redirector->gotoUrlAndExit($url->getRelativeUrl());
+ }
+
+ /**
+ * Redirect to a specific url, updating the browsers URL field
+ *
+ * @param Url|string $url The target to redirect to
+ **/
+ public function redirectNow($url)
+ {
+ if ($this->isXhr()) {
+ $this->redirectXhr($url);
+ } else {
+ $this->redirectHttp($url);
+ }
+ }
+
+ protected function shutdownSession()
+ {
+ $session = Session::getSession();
+ if ($session->hasChanged()) {
+ $session->write();
+ }
+ }
+
+ protected function rerenderLayout()
+ {
+ $this->rerenderLayout = true;
+ $this->xhrLayout = 'layout';
+ return $this;
+ }
+
+ protected function reloadCss()
+ {
+ $this->reloadCss = true;
+ return $this;
+ }
+}
diff --git a/vendor/gipfl/icingaweb2/src/Controller/Extension/AutoRefreshHelper.php b/vendor/gipfl/icingaweb2/src/Controller/Extension/AutoRefreshHelper.php
new file mode 100644
index 0000000..5b899ba
--- /dev/null
+++ b/vendor/gipfl/icingaweb2/src/Controller/Extension/AutoRefreshHelper.php
@@ -0,0 +1,30 @@
+<?php
+
+namespace gipfl\IcingaWeb2\Controller\Extension;
+
+use InvalidArgumentException;
+
+trait AutoRefreshHelper
+{
+ /** @var int|null */
+ private $autorefreshInterval;
+
+ public function setAutorefreshInterval($interval)
+ {
+ if (! is_int($interval) || $interval < 1) {
+ throw new InvalidArgumentException(
+ 'Setting autorefresh interval smaller than 1 second is not allowed'
+ );
+ }
+ $this->autorefreshInterval = $interval;
+ $this->layout->autorefreshInterval = $interval;
+ return $this;
+ }
+
+ public function disableAutoRefresh()
+ {
+ $this->autorefreshInterval = null;
+ $this->layout->autorefreshInterval = null;
+ return $this;
+ }
+}
diff --git a/vendor/gipfl/icingaweb2/src/Controller/Extension/ConfigHelper.php b/vendor/gipfl/icingaweb2/src/Controller/Extension/ConfigHelper.php
new file mode 100644
index 0000000..72cefa7
--- /dev/null
+++ b/vendor/gipfl/icingaweb2/src/Controller/Extension/ConfigHelper.php
@@ -0,0 +1,46 @@
+<?php
+
+namespace gipfl\IcingaWeb2\Controller\Extension;
+
+use Icinga\Application\Config;
+
+trait ConfigHelper
+{
+ private $config;
+
+ private $configs = [];
+
+ /**
+ * @param null $file
+ * @return Config
+ * @codingStandardsIgnoreStart
+ */
+ public function Config($file = null)
+ {
+ // @codingStandardsIgnoreEnd
+ if ($this->moduleName === null) {
+ if ($file === null) {
+ return Config::app();
+ } else {
+ return Config::app($file);
+ }
+ } else {
+ return $this->getModuleConfig($file);
+ }
+ }
+
+ public function getModuleConfig($file = null)
+ {
+ if ($file === null) {
+ if ($this->config === null) {
+ $this->config = Config::module($this->getModuleName());
+ }
+ return $this->config;
+ } else {
+ if (! array_key_exists($file, $this->configs)) {
+ $this->configs[$file] = Config::module($this->getModuleName(), $file);
+ }
+ return $this->configs[$file];
+ }
+ }
+}
diff --git a/vendor/gipfl/icingaweb2/src/Controller/Extension/ControlsAndContentHelper.php b/vendor/gipfl/icingaweb2/src/Controller/Extension/ControlsAndContentHelper.php
new file mode 100644
index 0000000..00ef389
--- /dev/null
+++ b/vendor/gipfl/icingaweb2/src/Controller/Extension/ControlsAndContentHelper.php
@@ -0,0 +1,173 @@
+<?php
+
+namespace gipfl\IcingaWeb2\Controller\Extension;
+
+use gipfl\IcingaWeb2\Url;
+use gipfl\IcingaWeb2\Widget\Content;
+use gipfl\IcingaWeb2\Widget\Controls;
+use gipfl\IcingaWeb2\Widget\Tabs;
+use ipl\Html\HtmlDocument;
+
+trait ControlsAndContentHelper
+{
+ /** @var Controls */
+ private $controls;
+
+ /** @var Content */
+ private $content;
+
+ protected $title;
+
+ /** @var Url */
+ private $url;
+
+ /** @var Url */
+ private $originalUrl;
+
+ /**
+ * TODO: Not sure whether we need dedicated Content/Controls classes,
+ * a simple Container with a class name might suffice here
+ *
+ * @return Controls
+ */
+ public function controls()
+ {
+ if ($this->controls === null) {
+ $this->view->controls = $this->controls = new Controls();
+ }
+
+ return $this->controls;
+ }
+
+ /**
+ * @param Tabs|null $tabs
+ * @return Tabs
+ */
+ public function tabs(Tabs $tabs = null)
+ {
+ if ($tabs === null) {
+ return $this->controls()->getTabs();
+ } else {
+ $this->controls()->setTabs($tabs);
+ return $tabs;
+ }
+ }
+
+ /**
+ * @param HtmlDocument|null $actionBar
+ * @return HtmlDocument
+ */
+ public function actions(HtmlDocument $actionBar = null)
+ {
+ if ($actionBar === null) {
+ return $this->controls()->getActionBar();
+ } else {
+ $this->controls()->setActionBar($actionBar);
+ return $actionBar;
+ }
+ }
+
+ /**
+ * @return Content
+ */
+ public function content()
+ {
+ if ($this->content === null) {
+ $this->view->content = $this->content = new Content();
+ }
+
+ return $this->content;
+ }
+
+ /**
+ * @param $title
+ * @return $this
+ */
+ public function setTitle($title)
+ {
+ $this->title = $this->makeTitle(func_get_args());
+ return $this;
+ }
+
+ /**
+ * @param $title
+ * @return $this
+ */
+ public function addTitle($title)
+ {
+ $title = $this->makeTitle(func_get_args());
+ $this->title = $title;
+ $this->controls()->addTitle($title);
+
+ return $this;
+ }
+
+ private function makeTitle($args)
+ {
+ $title = array_shift($args);
+
+ if (empty($args)) {
+ return $title;
+ } else {
+ return vsprintf($title, $args);
+ }
+ }
+
+ /**
+ * @param string $title
+ * @param mixed $url
+ * @param string $name
+ * @return $this
+ */
+ public function addSingleTab($title, $url = null, $name = 'main')
+ {
+ if ($url === null) {
+ $url = $this->url();
+ }
+
+ $this->tabs()->add($name, [
+ 'label' => $title,
+ 'url' => $url,
+ ])->activate($name);
+
+ return $this;
+ }
+
+ /**
+ * @return Url
+ */
+ public function url()
+ {
+ if ($this->url === null) {
+ $this->url = $this->getOriginalUrl();
+ }
+
+ return $this->url;
+ }
+
+ /**
+ * @return Url
+ */
+ public function getOriginalUrl()
+ {
+ if ($this->originalUrl === null) {
+ $this->originalUrl = clone($this->getUrlFromRequest());
+ }
+
+ return clone($this->originalUrl);
+ }
+
+ /**
+ * @return Url
+ */
+ protected function getUrlFromRequest()
+ {
+ /** @var \Icinga\Web\Request $request */
+ $request = $this->getRequest();
+ $webUrl = $request->getUrl();
+
+ return Url::fromPath(
+ $webUrl->getPath()
+ )->setParams($webUrl->getParams());
+ }
+}
diff --git a/vendor/gipfl/icingaweb2/src/Data/Paginatable.php b/vendor/gipfl/icingaweb2/src/Data/Paginatable.php
new file mode 100644
index 0000000..8cd3963
--- /dev/null
+++ b/vendor/gipfl/icingaweb2/src/Data/Paginatable.php
@@ -0,0 +1,64 @@
+<?php
+
+namespace gipfl\IcingaWeb2\Data;
+
+use Countable;
+
+interface Paginatable extends Countable
+{
+ /**
+ * Set a limit count and offset
+ *
+ * @param int $count Number of rows to return
+ * @param int $offset Skip that many rows
+ *
+ * @return self
+ */
+ public function limit($count = null, $offset = null);
+
+ /**
+ * Whether a limit is set
+ *
+ * @return bool
+ */
+ public function hasLimit();
+
+ /**
+ * Get the limit if any
+ *
+ * @return int|null
+ */
+ public function getLimit();
+
+ /**
+ * Set limit
+ *
+ * @param int $limit Number of rows to return
+ *
+ * @return int|null
+ */
+ public function setLimit($limit);
+
+ /**
+ * Whether an offset is set
+ *
+ * @return bool
+ */
+ public function hasOffset();
+
+ /**
+ * Get the offset if any
+ *
+ * @return int|null
+ */
+ public function getOffset();
+
+ /**
+ * Set offset
+ *
+ * @param int $offset Skip that many rows
+ *
+ * @return int|null
+ */
+ public function setOffset($offset);
+}
diff --git a/vendor/gipfl/icingaweb2/src/Data/SimpleQueryPaginationAdapter.php b/vendor/gipfl/icingaweb2/src/Data/SimpleQueryPaginationAdapter.php
new file mode 100644
index 0000000..d6a1b97
--- /dev/null
+++ b/vendor/gipfl/icingaweb2/src/Data/SimpleQueryPaginationAdapter.php
@@ -0,0 +1,68 @@
+<?php
+
+namespace gipfl\IcingaWeb2\Data;
+
+use Icinga\Application\Benchmark;
+use Icinga\Data\SimpleQuery;
+
+class SimpleQueryPaginationAdapter implements Paginatable
+{
+ /** @var SimpleQuery */
+ private $query;
+
+ public function __construct(SimpleQuery $query)
+ {
+ $this->query = $query;
+ }
+
+ #[\ReturnTypeWillChange]
+ public function count()
+ {
+ Benchmark::measure('Running count() for pagination');
+ $count = $this->query->count();
+ Benchmark::measure("Counted $count rows");
+
+ return $count;
+ }
+
+ public function limit($count = null, $offset = null)
+ {
+ $this->query->limit($count, $offset);
+ }
+
+ public function hasLimit()
+ {
+ return $this->getLimit() !== null;
+ }
+
+ public function getLimit()
+ {
+ return $this->query->getLimit();
+ }
+
+ public function setLimit($limit)
+ {
+ $this->query->limit(
+ $limit,
+ $this->getOffset()
+ );
+ }
+
+ public function hasOffset()
+ {
+ return $this->getOffset() !== null;
+ }
+
+ public function getOffset()
+ {
+ return $this->query->getOffset();
+ }
+
+ public function setOffset($offset)
+ {
+ $this->query->limit(
+ $this->getLimit(),
+ $offset
+ );
+ }
+}
diff --git a/vendor/gipfl/icingaweb2/src/FakeRequest.php b/vendor/gipfl/icingaweb2/src/FakeRequest.php
new file mode 100644
index 0000000..854c26e
--- /dev/null
+++ b/vendor/gipfl/icingaweb2/src/FakeRequest.php
@@ -0,0 +1,32 @@
+<?php
+
+namespace gipfl\IcingaWeb2;
+
+use Icinga\Web\Request;
+use RuntimeException;
+
+class FakeRequest extends Request
+{
+ /** @var string */
+ private static $baseUrl;
+
+ public static function setConfiguredBaseUrl($url)
+ {
+ self::$baseUrl = $url;
+ }
+
+ public function setUrl(Url $url)
+ {
+ $this->url = $url;
+ return $this;
+ }
+
+ public function getBaseUrl($raw = false)
+ {
+ if (self::$baseUrl === null) {
+ throw new RuntimeException('Cannot determine base URL on CLI if not configured');
+ } else {
+ return self::$baseUrl;
+ }
+ }
+}
diff --git a/vendor/gipfl/icingaweb2/src/Icon.php b/vendor/gipfl/icingaweb2/src/Icon.php
new file mode 100644
index 0000000..0001e12
--- /dev/null
+++ b/vendor/gipfl/icingaweb2/src/Icon.php
@@ -0,0 +1,27 @@
+<?php
+
+namespace gipfl\IcingaWeb2;
+
+use ipl\Html\BaseHtmlElement;
+
+class Icon extends BaseHtmlElement
+{
+ protected $tag = 'i';
+
+ public function __construct($name, $attributes = null)
+ {
+ $this->setAttributes($attributes);
+ $this->getAttributes()->add('class', array('icon', 'icon-' . $name));
+ }
+
+ /**
+ * @param string $name
+ * @param array $attributes
+ *
+ * @return static
+ */
+ public static function create($name, array $attributes = null)
+ {
+ return new static($name, $attributes);
+ }
+}
diff --git a/vendor/gipfl/icingaweb2/src/IconHelper.php b/vendor/gipfl/icingaweb2/src/IconHelper.php
new file mode 100644
index 0000000..1c8af9d
--- /dev/null
+++ b/vendor/gipfl/icingaweb2/src/IconHelper.php
@@ -0,0 +1,89 @@
+<?php
+
+namespace gipfl\IcingaWeb2;
+
+use InvalidArgumentException;
+
+/**
+ * Icon helper class
+ *
+ * Should help to reduce redundant icon-lookup code. Currently with hardcoded
+ * icons only, could easily provide support for all of them as follows:
+ *
+ * $confFile = Icinga::app()
+ * ->getApplicationDir('fonts/fontello-ifont/config.json');
+ *
+ * $font = json_decode(file_get_contents($confFile));
+ * // 'icon-' is to be found in $font->css_prefix_text
+ * foreach ($font->glyphs as $icon) {
+ * // $icon->css (= 'help') -> 0x . dechex($icon->code)
+ * }
+ */
+class IconHelper
+{
+ private $icons = [
+ 'minus' => 'e806',
+ 'trash' => 'e846',
+ 'plus' => 'e805',
+ 'cancel' => 'e804',
+ 'help' => 'e85b',
+ 'angle-double-right' => 'e87b',
+ 'up-big' => 'e825',
+ 'down-big' => 'e828',
+ 'down-open' => 'e821',
+ ];
+
+ private $mappedUtf8Icons;
+
+ private $reversedUtf8Icons;
+
+ private static $instance;
+
+ public function __construct()
+ {
+ $this->prepareIconMappings();
+ }
+
+ public static function instance()
+ {
+ if (self::$instance === null) {
+ self::$instance = new static;
+ }
+
+ return self::$instance;
+ }
+
+ public function characterIconName($character)
+ {
+ if (array_key_exists($character, $this->reversedUtf8Icons)) {
+ return $this->reversedUtf8Icons[$character];
+ } else {
+ throw new InvalidArgumentException('There is no mapping for the given character');
+ }
+ }
+
+ protected function hexToCharacter($hex)
+ {
+ return json_decode('"\u' . $hex . '"');
+ }
+
+ public function iconCharacter($name)
+ {
+ if (array_key_exists($name, $this->mappedUtf8Icons)) {
+ return $this->mappedUtf8Icons[$name];
+ } else {
+ return $this->mappedUtf8Icons['help'];
+ }
+ }
+
+ protected function prepareIconMappings()
+ {
+ $this->mappedUtf8Icons = [];
+ $this->reversedUtf8Icons = [];
+ foreach ($this->icons as $name => $hex) {
+ $character = $this->hexToCharacter($hex);
+ $this->mappedUtf8Icons[$name] = $character;
+ $this->reversedUtf8Icons[$character] = $name;
+ }
+ }
+}
diff --git a/vendor/gipfl/icingaweb2/src/Img.php b/vendor/gipfl/icingaweb2/src/Img.php
new file mode 100644
index 0000000..3c68adb
--- /dev/null
+++ b/vendor/gipfl/icingaweb2/src/Img.php
@@ -0,0 +1,83 @@
+<?php
+
+namespace gipfl\IcingaWeb2;
+
+use Icinga\Web\Url as WebUrl;
+use ipl\Html\Attribute;
+use ipl\Html\BaseHtmlElement;
+
+class Img extends BaseHtmlElement
+{
+ protected $tag = 'img';
+
+ /** @var Url */
+ protected $url;
+
+ protected $defaultAttributes = array('alt' => '');
+
+ protected function __construct()
+ {
+ }
+
+ /**
+ * @param Url|string $url
+ * @param array $urlParams
+ * @param array $attributes
+ *
+ * @return static
+ */
+ public static function create($url, $urlParams = null, array $attributes = null)
+ {
+ /** @var Img $img */
+ $img = new static();
+ $img->setAttributes($attributes);
+ $img->getAttributes()->registerAttributeCallback('src', array($img, 'getSrcAttribute'));
+ $img->setUrl($url, $urlParams);
+ return $img;
+ }
+
+ public function setUrl($url, $urlParams)
+ {
+ if ($url instanceof WebUrl) { // Hint: Url is also a WebUrl
+ if ($urlParams !== null) {
+ $url->addParams($urlParams);
+ }
+
+ $this->url = $url;
+ } else {
+ if ($urlParams === null) {
+ if (is_string($url) && substr($url, 0, 5) === 'data:') {
+ $this->url = $url;
+ return;
+ } else {
+ $this->url = Url::fromPath($url);
+ }
+ } else {
+ $this->url = Url::fromPath($url, $urlParams);
+ }
+ }
+
+ $this->url->getParams();
+ }
+
+ /**
+ * @return Attribute
+ */
+ public function getSrcAttribute()
+ {
+ if (is_string($this->url)) {
+ return new Attribute('src', $this->url);
+ } else {
+ return new Attribute('src', $this->getUrl()->getAbsoluteUrl('&'));
+ }
+ }
+
+ /**
+ * @return Url
+ */
+ public function getUrl()
+ {
+ // TODO: What if null? #?
+ return $this->url;
+ }
+}
diff --git a/vendor/gipfl/icingaweb2/src/Link.php b/vendor/gipfl/icingaweb2/src/Link.php
new file mode 100644
index 0000000..e6e4de9
--- /dev/null
+++ b/vendor/gipfl/icingaweb2/src/Link.php
@@ -0,0 +1,85 @@
+<?php
+
+namespace gipfl\IcingaWeb2;
+
+use Icinga\Web\Url as WebUrl;
+use ipl\Html\Attribute;
+use ipl\Html\BaseHtmlElement;
+use ipl\Html\ValidHtml;
+
+class Link extends BaseHtmlElement
+{
+ protected $tag = 'a';
+
+ /** @var Url */
+ protected $url;
+
+ /**
+ * Link constructor.
+ * @param $content
+ * @param $url
+ * @param null $urlParams
+ * @param array|null $attributes
+ */
+ public function __construct($content, $url, $urlParams = null, array $attributes = null)
+ {
+ $this->setContent($content);
+ $this->setAttributes($attributes);
+ $this->getAttributes()->registerAttributeCallback('href', array($this, 'getHrefAttribute'));
+ $this->setUrl($url, $urlParams);
+ }
+
+ /**
+ * @param ValidHtml|array|string $content
+ * @param Url|string $url
+ * @param array $urlParams
+ * @param mixed $attributes
+ *
+ * @return static
+ */
+ public static function create($content, $url, $urlParams = null, array $attributes = null)
+ {
+ $link = new static($content, $url, $urlParams, $attributes);
+ return $link;
+ }
+
+ /**
+ * @param $url
+ * @param $urlParams
+ */
+ public function setUrl($url, $urlParams)
+ {
+ if ($url instanceof WebUrl) { // Hint: Url is also a WebUrl
+ if ($urlParams !== null) {
+ $url->addParams($urlParams);
+ }
+
+ $this->url = $url;
+ } else {
+ if ($urlParams === null) {
+ $this->url = Url::fromPath($url);
+ } else {
+ $this->url = Url::fromPath($url, $urlParams);
+ }
+ }
+
+ $this->url->getParams();
+ }
+
+ /**
+ * @return Attribute
+ */
+ public function getHrefAttribute()
+ {
+ return new Attribute('href', $this->getUrl()->getAbsoluteUrl('&'));
+ }
+
+ /**
+ * @return Url
+ */
+ public function getUrl()
+ {
+ // TODO: What if null? #?
+ return $this->url;
+ }
+}
diff --git a/vendor/gipfl/icingaweb2/src/Table/Extension/MultiSelect.php b/vendor/gipfl/icingaweb2/src/Table/Extension/MultiSelect.php
new file mode 100644
index 0000000..7a5a3ff
--- /dev/null
+++ b/vendor/gipfl/icingaweb2/src/Table/Extension/MultiSelect.php
@@ -0,0 +1,29 @@
+<?php
+
+namespace gipfl\IcingaWeb2\Table\Extension;
+
+use gipfl\IcingaWeb2\Url;
+
+// Could also be a static method, MultiSelect::enable($table)
+trait MultiSelect
+{
+ protected function enableMultiSelect($url, $sourceUrl, array $keys)
+ {
+ /** @var $table \ipl\Html\BaseHtmlElement */
+ $table = $this;
+ $table->addAttributes([
+ 'class' => 'multiselect'
+ ]);
+
+ $prefix = 'data-icinga-multiselect';
+ $multi = [
+ "$prefix-url" => Url::fromPath($url),
+ "$prefix-controllers" => Url::fromPath($sourceUrl),
+ "$prefix-data" => implode(',', $keys),
+ ];
+
+ $table->addAttributes($multi);
+
+ return $this;
+ }
+}
diff --git a/vendor/gipfl/icingaweb2/src/Table/Extension/QuickSearch.php b/vendor/gipfl/icingaweb2/src/Table/Extension/QuickSearch.php
new file mode 100644
index 0000000..0f0cec3
--- /dev/null
+++ b/vendor/gipfl/icingaweb2/src/Table/Extension/QuickSearch.php
@@ -0,0 +1,74 @@
+<?php
+
+namespace gipfl\IcingaWeb2\Table\Extension;
+
+use gipfl\IcingaWeb2\Url;
+use gipfl\IcingaWeb2\Widget\Controls;
+use ipl\Html\BaseHtmlElement;
+use ipl\Html\Html;
+
+trait QuickSearch
+{
+ /** @var BaseHtmlElement */
+ private $quickSearchForm;
+
+ public function getQuickSearch(BaseHtmlElement $parent, Url $url)
+ {
+ $this->requireQuickSearchForm($parent, $url);
+ $search = $url->getParam('q');
+ return $search;
+ }
+
+ private function requireQuickSearchForm(BaseHtmlElement $parent, Url $url)
+ {
+ if ($this->quickSearchForm === null) {
+ $this->quickSearchForm = $this->buildQuickSearchForm($parent, $url);
+ }
+ }
+
+ private function buildQuickSearchForm(BaseHtmlElement $parent, Url $url)
+ {
+ $search = $url->getParam('q');
+
+ $form = Html::tag('form', [
+ 'action' => $url->without(array('q', 'page', 'modifyFilter'))->getAbsoluteUrl(),
+ 'class' => ['gipfl-quicksearch'],
+ 'method' => 'GET'
+ ]);
+
+ $form->add(
+ Html::tag('input', [
+ 'type' => 'text',
+ 'name' => 'q',
+ 'title' => $this->translate('Search is simple! Try to combine multiple words'),
+ 'value' => $search,
+ 'placeholder' => $this->translate('Search...'),
+ 'class' => 'search'
+ ])
+ );
+
+ $this->addQuickSearchToControls($parent, $form);
+
+ return $form;
+ }
+
+ protected function addQuickSearchToControls(BaseHtmlElement $parent, BaseHtmlElement $form)
+ {
+ if ($parent instanceof Controls) {
+ $title = $parent->getTitleElement();
+ if ($title === null) {
+ $parent->prepend($form);
+ } else {
+ $input = $form->getFirst('input');
+ $form->remove($input);
+ $title->add($input);
+ $form->add($title);
+ $parent->setTitleElement($form);
+ }
+ } else {
+ $parent->prepend($form);
+ }
+
+ return $this;
+ }
+}
diff --git a/vendor/gipfl/icingaweb2/src/Table/Extension/ZfSortablePriority.php b/vendor/gipfl/icingaweb2/src/Table/Extension/ZfSortablePriority.php
new file mode 100644
index 0000000..cb1eac6
--- /dev/null
+++ b/vendor/gipfl/icingaweb2/src/Table/Extension/ZfSortablePriority.php
@@ -0,0 +1,263 @@
+<?php
+
+namespace gipfl\IcingaWeb2\Table\Extension;
+
+use gipfl\IcingaWeb2\Table\ZfQueryBasedTable;
+use gipfl\IcingaWeb2\IconHelper;
+use gipfl\ZfDb\Exception\SelectException;
+use gipfl\ZfDb\Select;
+use Icinga\Web\Request;
+use Icinga\Web\Response;
+use ipl\Html\BaseHtmlElement;
+use ipl\Html\Html;
+use ipl\Html\HtmlString;
+use RuntimeException;
+use Zend_Db_Select_Exception as ZfDbSelectException;
+
+/**
+ * Trait ZfSortablePriority
+ *
+ * Assumes to run in a ZfQueryBasedTable
+ */
+trait ZfSortablePriority
+{
+ /** @var Request */
+ protected $request;
+
+ /** @var Response */
+ protected $response;
+
+ public function handleSortPriorityActions(Request $request, Response $response)
+ {
+ $this->request = $request;
+ $this->response = $response;
+ return $this;
+ }
+
+ protected function reallyHandleSortPriorityActions()
+ {
+ $request = $this->request;
+
+ if ($request->isPost() && $this->hasBeenSent($request)) {
+ // $this->fixPriorities();
+ foreach (array_keys($request->getPost()) as $key) {
+ if (substr($key, 0, 8) === 'MOVE_UP_') {
+ $id = (int) substr($key, 8);
+ $this->moveRow($id, 'up');
+ }
+ if (substr($key, 0, 10) === 'MOVE_DOWN_') {
+ $id = (int) substr($key, 10);
+ $this->moveRow($id, 'down');
+ }
+ }
+ $this->response->redirectAndExit($request->getUrl());
+ }
+ }
+
+ protected function hasBeenSent(Request $request)
+ {
+ return $request->getPost('__FORM_NAME') === $this->getUniqueFormName();
+ }
+
+ protected function addSortPriorityButtons(BaseHtmlElement $tr, $row)
+ {
+ $tr->add(
+ Html::tag(
+ 'td',
+ null,
+ $this->createUpDownButtons($row->{$this->getKeyColumn()})
+ )
+ );
+
+ return $tr;
+ }
+
+ protected function getKeyColumn()
+ {
+ if (isset($this->keyColumn)) {
+ return $this->keyColumn;
+ } else {
+ throw new RuntimeException(
+ 'ZfSortablePriority requires keyColumn'
+ );
+ }
+ }
+
+ protected function getPriorityColumn()
+ {
+ if (isset($this->priorityColumn)) {
+ return $this->priorityColumn;
+ } else {
+ throw new RuntimeException(
+ 'ZfSortablePriority requires priorityColumn'
+ );
+ }
+ }
+
+ protected function getPriorityColumns()
+ {
+ return [
+ 'id' => $this->getKeyColumn(),
+ 'prio' => $this->getPriorityColumn()
+ ];
+ }
+
+ protected function moveRow($id, $direction)
+ {
+ /** @var $this ZfQueryBasedTable */
+ $db = $this->db();
+ /** @var $this ZfQueryBasedTable */
+ $query = $this->getQuery();
+ $tableParts = $this->getQueryPart(Select::FROM);
+ $alias = key($tableParts);
+ $table = $tableParts[$alias]['tableName'];
+
+ $whereParts = $this->getQueryPart(Select::WHERE);
+ unset($query);
+ if (empty($whereParts)) {
+ $where = '';
+ } else {
+ $where = ' AND ' . implode(' ', $whereParts);
+ }
+
+ $prioCol = $this->getPriorityColumn();
+ $keyCol = $this->getKeyColumn();
+ $myPrio = (int) $db->fetchOne(
+ $db->select()
+ ->from($table, $prioCol)
+ ->where("$keyCol = ?", $id)
+ );
+
+ $op = $direction === 'up' ? '<' : '>';
+ $sortDir = $direction === 'up' ? 'DESC' : 'ASC';
+ $query = $db->select()
+ ->from([$alias => $table], $this->getPriorityColumns())
+ ->where("$prioCol $op ?", $myPrio)
+ ->order("$prioCol $sortDir")
+ ->limit(1);
+
+ if (! empty($whereParts)) {
+ $query->where(implode(' ', $whereParts));
+ }
+
+ $next = $db->fetchRow($query);
+
+ if ($next) {
+ $sql = 'UPDATE %s %s'
+ . ' SET %s = CASE WHEN %s = %s THEN %d ELSE %d END'
+ . ' WHERE %s IN (%s, %s)';
+
+ $query = sprintf(
+ $sql,
+ $table,
+ $alias,
+ $prioCol,
+ $keyCol,
+ $id,
+ (int) $next->prio,
+ $myPrio,
+ $keyCol,
+ $id,
+ (int) $next->id
+ ) . $where;
+
+ $db->query($query);
+ }
+ }
+
+ protected function getSortPriorityTitle()
+ {
+ /** @var ZfQueryBasedTable $table */
+ $table = $this;
+
+ return Html::tag(
+ 'span',
+ ['title' => $table->translate('Change priority')],
+ $table->translate('Prio')
+ );
+ }
+
+ protected function createUpDownButtons($key)
+ {
+ /** @var ZfQueryBasedTable $table */
+ $table = $this;
+ $up = $this->createIconButton(
+ "MOVE_UP_$key",
+ 'up-big',
+ $table->translate('Move up (raise priority)')
+ );
+ $down = $this->createIconButton(
+ "MOVE_DOWN_$key",
+ 'down-big',
+ $table->translate('Move down (lower priority)')
+ );
+
+ if ($table->isOnFirstRow()) {
+ $up->getAttributes()->add('disabled', 'disabled');
+ }
+
+ if ($table->isOnLastRow()) {
+ $down->getAttributes()->add('disabled', 'disabled');
+ }
+
+ return [$down, $up];
+ }
+
+ protected function createIconButton($key, $icon, $title)
+ {
+ return Html::tag('input', [
+ 'type' => 'submit',
+ 'class' => 'icon-button',
+ 'name' => $key,
+ 'title' => $title,
+ 'value' => IconHelper::instance()->iconCharacter($icon)
+ ]);
+ }
+
+ protected function getUniqueFormName()
+ {
+ $parts = explode('\\', get_class($this));
+ return end($parts);
+ }
+
+ protected function renderWithSortableForm()
+ {
+ if ($this->request === null) {
+ return parent::render();
+ }
+ $this->reallyHandleSortPriorityActions();
+
+ $url = $this->request->getUrl();
+ // TODO: No margin for form
+ $form = Html::tag('form', [
+ 'action' => $url->getAbsoluteUrl(),
+ 'method' => 'POST'
+ ], [
+ Html::tag('input', [
+ 'type' => 'hidden',
+ 'name' => '__FORM_NAME',
+ 'value' => $this->getUniqueFormName()
+ ]),
+ new HtmlString(parent::render())
+ ]);
+
+ return $form->render();
+ }
+
+ protected function getQueryPart($part)
+ {
+ /** @var ZfQueryBasedTable $table */
+ $table = $this;
+ /** @var Select|\Zend_Db_Select $query */
+ $query = $table->getQuery();
+ try {
+ return $query->getPart($part);
+ } catch (SelectException $e) {
+ // Will not happen if $part is correct.
+ throw new RuntimeException($e);
+ } catch (ZfDbSelectException $e) {
+ // Will not happen if $part is correct.
+ throw new RuntimeException($e);
+ }
+ }
+}
diff --git a/vendor/gipfl/icingaweb2/src/Table/QueryBasedTable.php b/vendor/gipfl/icingaweb2/src/Table/QueryBasedTable.php
new file mode 100644
index 0000000..e9281c7
--- /dev/null
+++ b/vendor/gipfl/icingaweb2/src/Table/QueryBasedTable.php
@@ -0,0 +1,281 @@
+<?php
+
+namespace gipfl\IcingaWeb2\Table;
+
+use Countable;
+use gipfl\Format\LocalDateFormat;
+use gipfl\IcingaWeb2\Data\Paginatable;
+use gipfl\IcingaWeb2\Zf1\Db\FilterRenderer;
+use gipfl\IcingaWeb2\Table\Extension\QuickSearch;
+use gipfl\IcingaWeb2\Url;
+use gipfl\IcingaWeb2\Widget\ControlsAndContent;
+use gipfl\IcingaWeb2\Widget\Paginator;
+use gipfl\Translation\TranslationHelper;
+use Icinga\Application\Benchmark;
+use Icinga\Data\Filter\Filter;
+use ipl\Html\Table;
+
+abstract class QueryBasedTable extends Table implements Countable
+{
+ use TranslationHelper;
+ use QuickSearch;
+
+ protected $defaultAttributes = [
+ 'class' => ['common-table', 'table-row-selectable'],
+ 'data-base-target' => '_next',
+ ];
+
+ private $fetchedRows;
+
+ private $firstRow;
+
+ private $lastRow = false;
+
+ private $rowNumber;
+
+ private $rowNumberOnPage;
+
+ protected $lastDay;
+
+ /** @var Paginator|null Will usually be defined at rendering time */
+ protected $paginator;
+
+ private $isUsEnglish;
+
+ private $dateFormatter;
+
+ protected $searchColumns = [];
+
+ /**
+ * @return Paginatable
+ */
+ abstract protected function getPaginationAdapter();
+
+ abstract public function getQuery();
+
+ public function getPaginator(Url $url)
+ {
+ return new Paginator(
+ $this->getPaginationAdapter(),
+ $url
+ );
+ }
+
+ public function count()
+ {
+ return $this->getPaginationAdapter()->count();
+ }
+
+ public function applyFilter(Filter $filter)
+ {
+ FilterRenderer::applyToQuery($filter, $this->getQuery());
+ return $this;
+ }
+
+ protected function getSearchColumns()
+ {
+ return $this->searchColumns;
+ }
+
+ public function search($search)
+ {
+ if (! empty($search)) {
+ $query = $this->getQuery();
+ $columns = $this->getSearchColumns();
+ if (strpos($search, ' ') === false) {
+ $filter = Filter::matchAny();
+ foreach ($columns as $column) {
+ $filter->addFilter(Filter::expression($column, '=', "*$search*"));
+ }
+ } else {
+ $filter = Filter::matchAll();
+ foreach (explode(' ', $search) as $s) {
+ $sub = Filter::matchAny();
+ foreach ($columns as $column) {
+ $sub->addFilter(Filter::expression($column, '=', "*$s*"));
+ }
+ $filter->addFilter($sub);
+ }
+ }
+
+ FilterRenderer::applyToQuery($filter, $query);
+ }
+
+ return $this;
+ }
+
+ abstract protected function prepareQuery();
+
+ public function renderContent()
+ {
+ $titleColumns = $this->renderTitleColumns();
+ if ($titleColumns) {
+ $this->getHeader()->add($titleColumns);
+ }
+ $this->fetchRows();
+
+ return parent::renderContent();
+ }
+
+ protected function renderTitleColumns()
+ {
+ // TODO: drop this
+ if (method_exists($this, 'getColumnsToBeRendered')) {
+ $columns = $this->getColumnsToBeRendered();
+ if (isset($columns) && count($columns)) {
+ return static::row($columns, null, 'th');
+ }
+ }
+
+ return null;
+ }
+
+ protected function splitByDay($timestamp)
+ {
+ $this->renderDayIfNew((int) $timestamp);
+ }
+
+ public function isOnFirstPage()
+ {
+ if ($this->paginator === null) {
+ // No paginator? Then there should be only a single page
+ return true;
+ }
+
+ return $this->paginator->getCurrentPage() === 1;
+ }
+
+ public function isOnFirstRow()
+ {
+ return $this->firstRow === true;
+ }
+
+ public function isOnLastRow()
+ {
+ return $this->lastRow === true;
+ }
+
+ protected function fetchRows()
+ {
+ $firstPage = $this->isOnFirstPage();
+ $this->rowNumberOnPage = 0;
+ $this->rowNumber = $this->getPaginationAdapter()->getOffset();
+ $lastRow = count($this);
+ foreach ($this->fetch() as $row) {
+ $this->rowNumber++;
+ $this->rowNumberOnPage++;
+ if (null === $this->firstRow) {
+ if ($firstPage) {
+ $this->firstRow = true;
+ } else {
+ $this->firstRow = false;
+ }
+ } elseif (true === $this->firstRow) {
+ $this->firstRow = false;
+ }
+ if ($lastRow === $this->rowNumber) {
+ $this->lastRow = true;
+ }
+ // Hint: do not fetch the body first, the row might want to replace it
+ $tr = $this->renderRow($row);
+ $this->add($tr);
+ }
+ }
+
+ protected function renderRow($row)
+ {
+ return $this::row([$row]);
+ }
+
+ /**
+ * @deprecated
+ * @return bool
+ */
+ protected function isUsEnglish()
+ {
+ if ($this->isUsEnglish === null) {
+ $this->isUsEnglish = in_array(setlocale(LC_ALL, 0), ['en_US', 'en_US.UTF-8', 'C']);
+ }
+
+ return $this->isUsEnglish;
+ }
+
+ /**
+ * @param int $timestamp
+ */
+ protected function renderDayIfNew($timestamp)
+ {
+ $day = $this->getDateFormatter()->getFullDay($timestamp);
+
+ if ($this->lastDay !== $day) {
+ $this->nextHeader()->add(
+ $this::th($day, [
+ 'colspan' => 2,
+ 'class' => 'table-header-day'
+ ])
+ );
+
+ $this->lastDay = $day;
+ $this->nextBody();
+ }
+ }
+
+ abstract protected function fetchQueryRows();
+
+ public function fetch()
+ {
+ $parts = explode('\\', get_class($this));
+ $name = end($parts);
+ Benchmark::measure("Fetching data for $name table");
+ $rows = $this->fetchQueryRows();
+ $this->fetchedRows = count($rows);
+ Benchmark::measure("Fetched $this->fetchedRows rows for $name table");
+
+ return $rows;
+ }
+
+ protected function initializeOptionalQuickSearch(ControlsAndContent $controller)
+ {
+ $columns = $this->getSearchColumns();
+ if (! empty($columns)) {
+ $this->search(
+ $this->getQuickSearch(
+ $controller->controls(),
+ $controller->url()
+ )
+ );
+ }
+ }
+
+ /**
+ * @param ControlsAndContent $controller
+ * @return $this
+ */
+ public function renderTo(ControlsAndContent $controller)
+ {
+ $url = $controller->url();
+ $c = $controller->content();
+ $this->paginator = $this->getPaginator($url);
+ $this->initializeOptionalQuickSearch($controller);
+ $controller->actions()->add($this->paginator);
+ $c->add($this);
+
+ // TODO: move elsewhere
+ if (method_exists($this, 'dumpSqlQuery')) {
+ if ($url->getParam('format') === 'sql') {
+ $c->prepend($this->dumpSqlQuery($url));
+ }
+ }
+
+ return $this;
+ }
+
+ protected function getDateFormatter()
+ {
+ if ($this->dateFormatter === null) {
+ $this->dateFormatter = new LocalDateFormat();
+ }
+
+ return $this->dateFormatter;
+ }
+}
diff --git a/vendor/gipfl/icingaweb2/src/Table/SimpleQueryBasedTable.php b/vendor/gipfl/icingaweb2/src/Table/SimpleQueryBasedTable.php
new file mode 100644
index 0000000..8d6015a
--- /dev/null
+++ b/vendor/gipfl/icingaweb2/src/Table/SimpleQueryBasedTable.php
@@ -0,0 +1,34 @@
+<?php
+
+namespace gipfl\IcingaWeb2\Table;
+
+use Icinga\Data\SimpleQuery;
+use gipfl\IcingaWeb2\Data\SimpleQueryPaginationAdapter;
+
+abstract class SimpleQueryBasedTable extends QueryBasedTable
+{
+ /** @var SimpleQuery */
+ private $query;
+
+ protected function getPaginationAdapter()
+ {
+ return new SimpleQueryPaginationAdapter($this->getQuery());
+ }
+
+ protected function fetchQueryRows()
+ {
+ return $this->query->fetchAll();
+ }
+
+ /**
+ * @return SimpleQuery
+ */
+ public function getQuery()
+ {
+ if ($this->query === null) {
+ $this->query = $this->prepareQuery();
+ }
+
+ return $this->query;
+ }
+}
diff --git a/vendor/gipfl/icingaweb2/src/Table/ZfQueryBasedTable.php b/vendor/gipfl/icingaweb2/src/Table/ZfQueryBasedTable.php
new file mode 100644
index 0000000..8a421d5
--- /dev/null
+++ b/vendor/gipfl/icingaweb2/src/Table/ZfQueryBasedTable.php
@@ -0,0 +1,149 @@
+<?php
+
+namespace gipfl\IcingaWeb2\Table;
+
+use gipfl\IcingaWeb2\Link;
+use gipfl\IcingaWeb2\Url;
+use gipfl\IcingaWeb2\Widget\ControlsAndContent;
+use gipfl\IcingaWeb2\Zf1\Db\FilterRenderer;
+use gipfl\IcingaWeb2\Zf1\Db\SelectPaginationAdapter;
+use gipfl\ZfDb\Adapter\Adapter as Db;
+use gipfl\ZfDb\Select as DbSelect;
+use Icinga\Data\Db\DbConnection;
+use Icinga\Data\Filter\Filter;
+use ipl\Html\DeferredText;
+use ipl\Html\Html;
+use LogicException;
+use Zend_Db_Adapter_Abstract as DbAdapter;
+
+abstract class ZfQueryBasedTable extends QueryBasedTable
+{
+ /** @var ?DbConnection */
+ private $connection;
+
+ /** @var DbAdapter|Db */
+ private $db;
+
+ private $query;
+
+ private $paginationAdapter;
+
+ public function __construct($db)
+ {
+ if ($db instanceof Db || $db instanceof DbAdapter) {
+ $this->db = $db;
+ } elseif ($db instanceof DbConnection) {
+ $this->connection = $db;
+ $this->db = $db->getDbAdapter();
+ } else {
+ throw new LogicException(sprintf(
+ 'Unable to deal with %s db class',
+ get_class($db)
+ ));
+ }
+ }
+
+ public static function show(ControlsAndContent $controller, DbConnection $db)
+ {
+ $table = new static($db);
+ $table->renderTo($controller);
+ }
+
+ public function getCountQuery()
+ {
+ return $this->getPaginationAdapter()->getCountQuery();
+ }
+
+ protected function getPaginationAdapter()
+ {
+ if ($this->paginationAdapter === null) {
+ $this->paginationAdapter = new SelectPaginationAdapter($this->getQuery());
+ }
+
+ return $this->paginationAdapter;
+ }
+
+ public function applyFilter(Filter $filter)
+ {
+ FilterRenderer::applyToQuery($filter, $this->getQuery());
+ return $this;
+ }
+
+ public function search($search)
+ {
+ if (! empty($search)) {
+ $query = $this->getQuery();
+ $columns = $this->getSearchColumns();
+ if (strpos($search, ' ') === false) {
+ $filter = Filter::matchAny();
+ foreach ($columns as $column) {
+ $filter->addFilter(Filter::expression($column, '=', "*$search*"));
+ }
+ } else {
+ $filter = Filter::matchAll();
+ foreach (explode(' ', $search) as $s) {
+ $sub = Filter::matchAny();
+ foreach ($columns as $column) {
+ $sub->addFilter(Filter::expression($column, '=', "*$s*"));
+ }
+ $filter->addFilter($sub);
+ }
+ }
+
+ FilterRenderer::applyToQuery($filter, $query);
+ }
+
+ return $this;
+ }
+
+ protected function fetchQueryRows()
+ {
+ return $this->db->fetchAll($this->getQuery());
+ }
+
+ /**
+ * @deprecated Might be null, we'll fade it out
+ * @return ?DbConnection
+ */
+ public function connection()
+ {
+ return $this->connection;
+ }
+
+ public function db()
+ {
+ return $this->db;
+ }
+
+ /**
+ * @return DbSelect|\Zend_Db_Select
+ */
+ public function getQuery()
+ {
+ if ($this->query === null) {
+ $this->query = $this->prepareQuery();
+ }
+
+ return $this->query;
+ }
+
+ public function dumpSqlQuery(Url $url)
+ {
+ $self = $this;
+ return Html::tag('div', ['class' => 'sql-dump'], [
+ Link::create('[ close ]', $url->without('format')),
+ Html::tag('h3', null, $this->translate('SQL Query')),
+ Html::tag('pre', null, new DeferredText(
+ function () use ($self) {
+ return wordwrap($self->getQuery());
+ }
+ )),
+ Html::tag('h3', null, $this->translate('Count Query')),
+ Html::tag('pre', null, new DeferredText(
+ function () use ($self) {
+ return wordwrap($self->getCountQuery());
+ }
+ )),
+ ]);
+ }
+}
diff --git a/vendor/gipfl/icingaweb2/src/Translator.php b/vendor/gipfl/icingaweb2/src/Translator.php
new file mode 100644
index 0000000..b1cb088
--- /dev/null
+++ b/vendor/gipfl/icingaweb2/src/Translator.php
@@ -0,0 +1,26 @@
+<?php
+
+namespace gipfl\IcingaWeb2;
+
+use gipfl\Translation\TranslatorInterface;
+
+class Translator implements TranslatorInterface
+{
+ /** @var string */
+ private $domain;
+
+ public function __construct($domain)
+ {
+ $this->domain = $domain;
+ }
+
+ public function translate($string)
+ {
+ $res = dgettext($this->domain, $string);
+ if ($res === $string && $this->domain !== 'icinga') {
+ return dgettext('icinga', $string);
+ }
+
+ return $res;
+ }
+}
diff --git a/vendor/gipfl/icingaweb2/src/Url.php b/vendor/gipfl/icingaweb2/src/Url.php
new file mode 100644
index 0000000..2c6bf1f
--- /dev/null
+++ b/vendor/gipfl/icingaweb2/src/Url.php
@@ -0,0 +1,162 @@
+<?php
+
+namespace gipfl\IcingaWeb2;
+
+use Exception;
+use Icinga\Application\Icinga;
+use Icinga\Exception\ProgrammingError;
+use Icinga\Web\Url as WebUrl;
+use Icinga\Web\UrlParams;
+use InvalidArgumentException;
+use Psr\Http\Message\ServerRequestInterface;
+use Psr\Http\Message\UriInterface;
+use RuntimeException;
+
+/**
+ * Class Url
+ *
+ * The main purpose of this class is to get unit tests running on CLI
+ * Little code from Icinga\Web\Url has been duplicated, as neither fromPath()
+ * nor getRequest() can be extended in a meaningful way at the time of this
+ * writing
+ */
+class Url extends WebUrl
+{
+ /**
+ * @param string $url
+ * @param array $params
+ * @param null $request
+ * @return Url
+ */
+ public static function fromPath($url, array $params = array(), $request = null)
+ {
+ if ($request === null) {
+ $request = static::getRequest();
+ }
+
+ if (! \is_string($url)) {
+ throw new InvalidArgumentException(sprintf(
+ 'url "%s" is not a string',
+ \var_export($url, 1)
+ ));
+ }
+
+ $self = new static;
+
+ if ($url === '#') {
+ return $self->setPath($url);
+ }
+
+ $parts = \parse_url($url);
+
+ $self->setBasePath($request->getBaseUrl());
+ if (isset($parts['path'])) {
+ $self->setPath($parts['path']);
+ }
+
+ if (isset($parts['query'])) {
+ $params = UrlParams::fromQueryString($parts['query'])->mergeValues($params);
+ }
+
+ if (isset($parts['fragment'])) {
+ $self->setAnchor($parts['fragment']);
+ }
+
+ $self->setParams($params);
+ return $self;
+ }
+
+ public static function fromUri(UriInterface $uri)
+ {
+ $query = $uri->getQuery();
+ $path = $uri->getPath();
+ if (\strlen($query)) {
+ $path .= "?$query";
+ }
+
+ return static::fromPath($path);
+ }
+
+ public static function fromServerRequest(ServerRequestInterface $request)
+ {
+ return static::fromUri($request->getUri());
+ }
+
+ /**
+ * Create a new Url class representing the current request
+ *
+ * If $params are given, those will be added to the request's parameters
+ * and overwrite any existing parameters
+ *
+ * @param UrlParams|array $params Parameters that should additionally be considered for the url
+ * @param \Icinga\Web\Request $request A request to use instead of the default one
+ *
+ * @return Url
+ */
+ public static function fromRequest($params = array(), $request = null)
+ {
+ if ($request === null) {
+ $request = static::getRequest();
+ }
+
+ $url = new Url();
+ $url->setPath(\ltrim($request->getPathInfo(), '/'));
+ $request->getQuery();
+
+ // $urlParams = UrlParams::fromQueryString($request->getQuery());
+ if (isset($_SERVER['QUERY_STRING'])) {
+ $urlParams = UrlParams::fromQueryString($_SERVER['QUERY_STRING']);
+ } else {
+ $urlParams = UrlParams::fromQueryString('');
+ foreach ($request->getQuery() as $k => $v) {
+ $urlParams->set($k, $v);
+ }
+ }
+
+ foreach ($params as $k => $v) {
+ $urlParams->set($k, $v);
+ }
+ $url->setParams($urlParams);
+ $url->setBasePath($request->getBaseUrl());
+
+ return $url;
+ }
+
+ public function setBasePath($basePath)
+ {
+ if (property_exists($this, 'basePath')) {
+ parent::setBasePath($basePath);
+ } else {
+ $this->setBaseUrl($basePath);
+ }
+
+ return $this;
+ }
+
+ public function setParams($params)
+ {
+ try {
+ return parent::setParams($params);
+ } catch (ProgrammingError $e) {
+ throw new InvalidArgumentException($e->getMessage(), 0, $e);
+ }
+ }
+
+ protected static function getRequest()
+ {
+ try {
+ $app = Icinga::app();
+ } catch (ProgrammingError $e) {
+ throw new RuntimeException($e->getMessage(), 0, $e);
+ }
+ if ($app->isCli()) {
+ try {
+ return new FakeRequest();
+ } catch (Exception $e) {
+ throw new RuntimeException($e->getMessage(), 0, $e);
+ }
+ } else {
+ return $app->getRequest();
+ }
+ }
+}
diff --git a/vendor/gipfl/icingaweb2/src/Widget/ActionBar.php b/vendor/gipfl/icingaweb2/src/Widget/ActionBar.php
new file mode 100644
index 0000000..63e6c77
--- /dev/null
+++ b/vendor/gipfl/icingaweb2/src/Widget/ActionBar.php
@@ -0,0 +1,25 @@
+<?php
+
+namespace gipfl\IcingaWeb2\Widget;
+
+use ipl\Html\BaseHtmlElement;
+
+class ActionBar extends BaseHtmlElement
+{
+ protected $contentSeparator = ' ';
+
+ /** @var string */
+ protected $tag = 'div';
+
+ protected $defaultAttributes = ['class' => 'gipfl-action-bar'];
+
+ /**
+ * @param string $target
+ * @return $this
+ */
+ public function setBaseTarget($target)
+ {
+ $this->getAttributes()->set('data-base-target', $target);
+ return $this;
+ }
+}
diff --git a/vendor/gipfl/icingaweb2/src/Widget/Content.php b/vendor/gipfl/icingaweb2/src/Widget/Content.php
new file mode 100644
index 0000000..92ea115
--- /dev/null
+++ b/vendor/gipfl/icingaweb2/src/Widget/Content.php
@@ -0,0 +1,14 @@
+<?php
+
+namespace gipfl\IcingaWeb2\Widget;
+
+use ipl\Html\BaseHtmlElement;
+
+class Content extends BaseHtmlElement
+{
+ protected $tag = 'div';
+
+ protected $contentSeparator = "\n";
+
+ protected $defaultAttributes = ['class' => 'content'];
+}
diff --git a/vendor/gipfl/icingaweb2/src/Widget/Controls.php b/vendor/gipfl/icingaweb2/src/Widget/Controls.php
new file mode 100644
index 0000000..cb52013
--- /dev/null
+++ b/vendor/gipfl/icingaweb2/src/Widget/Controls.php
@@ -0,0 +1,163 @@
+<?php
+
+namespace gipfl\IcingaWeb2\Widget;
+
+use ipl\Html\BaseHtmlElement;
+use ipl\Html\Html;
+use ipl\Html\HtmlDocument;
+
+class Controls extends BaseHtmlElement
+{
+ protected $tag = 'div';
+
+ protected $contentSeparator = "\n";
+
+ protected $defaultAttributes = ['class' => 'controls'];
+
+ /** @var Tabs */
+ private $tabs;
+
+ /** @var ActionBar */
+ private $actions;
+
+ /** @var string */
+ private $title;
+
+ /** @var string */
+ private $subTitle;
+
+ /** @var BaseHtmlElement */
+ private $titleElement;
+
+ /**
+ * @param $title
+ * @param null $subTitle
+ * @return $this
+ */
+ public function addTitle($title, $subTitle = null)
+ {
+ $this->title = $title;
+ if ($subTitle !== null) {
+ $this->subTitle = $subTitle;
+ }
+
+ return $this->setTitleElement($this->renderTitleElement());
+ }
+
+ /**
+ * @param BaseHtmlElement $element
+ * @return $this
+ */
+ public function setTitleElement(BaseHtmlElement $element)
+ {
+ if ($this->titleElement !== null) {
+ $this->remove($this->titleElement);
+ }
+
+ $this->titleElement = $element;
+ $this->prepend($element);
+
+ return $this;
+ }
+
+ public function getTitleElement()
+ {
+ return $this->titleElement;
+ }
+
+ /**
+ * @return Tabs
+ */
+ public function getTabs()
+ {
+ if ($this->tabs === null) {
+ $this->tabs = new Tabs();
+ }
+
+ return $this->tabs;
+ }
+
+ /**
+ * @param Tabs $tabs
+ * @return $this
+ */
+ public function setTabs(Tabs $tabs)
+ {
+ $this->tabs = $tabs;
+ return $this;
+ }
+
+ /**
+ * @param Tabs $tabs
+ * @return $this
+ */
+ public function prependTabs(Tabs $tabs)
+ {
+ if ($this->tabs === null) {
+ $this->tabs = $tabs;
+ } else {
+ $current = $this->tabs->getTabs();
+ $this->tabs = $tabs;
+ foreach ($current as $name => $tab) {
+ $this->tabs->add($name, $tab);
+ }
+ }
+
+ return $this;
+ }
+
+ /**
+ * @return ActionBar
+ */
+ public function getActionBar()
+ {
+ if ($this->actions === null) {
+ $this->setActionBar(new ActionBar());
+ }
+
+ return $this->actions;
+ }
+
+ /**
+ * @param HtmlDocument $actionBar
+ * @return $this
+ */
+ public function setActionBar(HtmlDocument $actionBar)
+ {
+ if ($this->actions !== null) {
+ $this->remove($this->actions);
+ }
+
+ $this->actions = $actionBar;
+ $this->add($actionBar);
+
+ return $this;
+ }
+
+ /**
+ * @return BaseHtmlElement
+ */
+ protected function renderTitleElement()
+ {
+ $h1 = Html::tag('h1', null, $this->title);
+ if ($this->subTitle) {
+ $h1->setSeparator(' ')->add(
+ Html::tag('small', null, $this->subTitle)
+ );
+ }
+
+ return $h1;
+ }
+
+ /**
+ * @return string
+ */
+ public function renderContent()
+ {
+ if (null !== $this->tabs) {
+ $this->prepend($this->tabs);
+ }
+
+ return parent::renderContent();
+ }
+}
diff --git a/vendor/gipfl/icingaweb2/src/Widget/ControlsAndContent.php b/vendor/gipfl/icingaweb2/src/Widget/ControlsAndContent.php
new file mode 100644
index 0000000..8574ce7
--- /dev/null
+++ b/vendor/gipfl/icingaweb2/src/Widget/ControlsAndContent.php
@@ -0,0 +1,60 @@
+<?php
+
+namespace gipfl\IcingaWeb2\Widget;
+
+use ipl\Html\HtmlDocument;
+use gipfl\IcingaWeb2\Url;
+
+interface ControlsAndContent
+{
+ /**
+ * @return Controls
+ */
+ public function controls();
+
+ /**
+ * @return Tabs
+ */
+ public function tabs();
+
+ /**
+ * @param HtmlDocument|null $actionBar
+ * @return HtmlDocument
+ */
+ public function actions(HtmlDocument $actionBar = null);
+
+ /**
+ * @return Content
+ */
+ public function content();
+
+ /**
+ * @param $title
+ * @return $this
+ */
+ public function setTitle($title);
+
+ /**
+ * @param $title
+ * @return $this
+ */
+ public function addTitle($title);
+
+ /**
+ * @param $title
+ * @param null $url
+ * @param string $name
+ * @return $this
+ */
+ public function addSingleTab($title, $url = null, $name = 'main');
+
+ /**
+ * @return Url
+ */
+ public function url();
+
+ /**
+ * @return Url
+ */
+ public function getOriginalUrl();
+}
diff --git a/vendor/gipfl/icingaweb2/src/Widget/ListItem.php b/vendor/gipfl/icingaweb2/src/Widget/ListItem.php
new file mode 100644
index 0000000..fa4b562
--- /dev/null
+++ b/vendor/gipfl/icingaweb2/src/Widget/ListItem.php
@@ -0,0 +1,26 @@
+<?php
+
+namespace gipfl\IcingaWeb2\Widget;
+
+use ipl\Html\Attributes;
+use ipl\Html\BaseHtmlElement;
+use ipl\Html\Html;
+use ipl\Html\ValidHtml;
+
+class ListItem extends BaseHtmlElement
+{
+ protected $contentSeparator = "\n";
+
+ /**
+ * @param ValidHtml|array|string $content
+ * @param Attributes|array $attributes
+ *
+ * @return $this
+ */
+ public function addItem($content, $attributes = null)
+ {
+ return $this->add(
+ Html::tag('li', $attributes, $content)
+ );
+ }
+}
diff --git a/vendor/gipfl/icingaweb2/src/Widget/NameValueTable.php b/vendor/gipfl/icingaweb2/src/Widget/NameValueTable.php
new file mode 100644
index 0000000..971a833
--- /dev/null
+++ b/vendor/gipfl/icingaweb2/src/Widget/NameValueTable.php
@@ -0,0 +1,29 @@
+<?php
+
+namespace gipfl\IcingaWeb2\Widget;
+
+use ipl\Html\Table;
+
+class NameValueTable extends Table
+{
+ protected $defaultAttributes = ['class' => 'name-value-table'];
+
+ public function createNameValueRow($name, $value)
+ {
+ return $this::tr([$this::th($name), $this::td($value)]);
+ }
+
+ public function addNameValueRow($name, $value)
+ {
+ return $this->add($this->createNameValueRow($name, $value));
+ }
+
+ public function addNameValuePairs($pairs)
+ {
+ foreach ($pairs as $name => $value) {
+ $this->addNameValueRow($name, $value);
+ }
+
+ return $this;
+ }
+}
diff --git a/vendor/gipfl/icingaweb2/src/Widget/Paginator.php b/vendor/gipfl/icingaweb2/src/Widget/Paginator.php
new file mode 100644
index 0000000..3c255a7
--- /dev/null
+++ b/vendor/gipfl/icingaweb2/src/Widget/Paginator.php
@@ -0,0 +1,463 @@
+<?php
+
+namespace gipfl\IcingaWeb2\Widget;
+
+use Icinga\Exception\ProgrammingError;
+use gipfl\IcingaWeb2\Data\Paginatable;
+use gipfl\IcingaWeb2\Icon;
+use gipfl\IcingaWeb2\Link;
+use gipfl\IcingaWeb2\Url;
+use gipfl\Translation\TranslationHelper;
+use ipl\Html\BaseHtmlElement;
+use ipl\Html\Html;
+
+class Paginator extends BaseHtmlElement
+{
+ use TranslationHelper;
+
+ protected $tag = 'div';
+
+ protected $defaultAttributes = [
+ 'class' => 'pagination-control',
+ 'role' => 'navigation',
+ ];
+
+ /** @var Paginatable The query the paginator widget is created for */
+ protected $query;
+
+ /** @var int */
+ protected $pageCount;
+
+ /** @var int */
+ protected $currentCount;
+
+ /** @var Url */
+ protected $url;
+
+ /** @var string */
+ protected $pageParam;
+
+ /** @var string */
+ protected $perPageParam;
+
+ /** @var int */
+ protected $totalCount;
+
+ /** @var int */
+ protected $defaultItemCountPerPage = 25;
+
+ public function __construct(
+ Paginatable $query,
+ Url $url,
+ $pageParameter = 'page',
+ $perPageParameter = 'limit'
+ ) {
+ $this->query = $query;
+ $this->setPageParam($pageParameter);
+ $this->setPerPageParam($perPageParameter);
+ $this->setUrl($url);
+ }
+
+ public function setItemsPerPage($count)
+ {
+ // TODO: this should become setOffset once available
+ $query = $this->getQuery();
+ $query->setLimit($count);
+
+ return $this;
+ }
+
+ protected function setPageParam($pageParam)
+ {
+ $this->pageParam = $pageParam;
+ return $this;
+ }
+
+ protected function setPerPageParam($perPageParam)
+ {
+ $this->perPageParam = $perPageParam;
+ return $this;
+ }
+
+ public function getPageParam()
+ {
+ return $this->pageParam;
+ }
+
+ public function getPerPageParam()
+ {
+ return $this->perPageParam;
+ }
+
+ public function getCurrentPage()
+ {
+ $query = $this->getQuery();
+ if ($query->hasOffset()) {
+ return ($query->getOffset() / $this->getItemsPerPage()) + 1;
+ } else {
+ return 1;
+ }
+ }
+
+ protected function setCurrentPage($page)
+ {
+ $page = (int) $page;
+ $offset = $this->firstRowOnPage($page) - 1;
+ if ($page > 1) {
+ $query = $this->getQuery();
+ $query->setOffset($offset);
+ }
+ }
+
+ public function getPageCount()
+ {
+ if ($this->pageCount === null) {
+ $this->pageCount = (int) ceil($this->getTotalItemCount() / $this->getItemsPerPage());
+ }
+
+ return $this->pageCount;
+ }
+
+ protected function getItemsPerPage()
+ {
+ $limit = $this->getQuery()->getLimit();
+ if ($limit === null) {
+ throw new ProgrammingError('Something went wrong, got no limit when there should be one');
+ } else {
+ return $limit;
+ }
+ }
+
+ public function getTotalItemCount()
+ {
+ if ($this->totalCount === null) {
+ $this->totalCount = count($this->getQuery());
+ }
+
+ return $this->totalCount;
+ }
+
+ public function getPrevious()
+ {
+ if ($this->hasPrevious()) {
+ return $this->getCurrentPage() - 1;
+ } else {
+ return null;
+ }
+ }
+
+ public function hasPrevious()
+ {
+ return $this->getCurrentPage() > 1;
+ }
+
+ public function getNext()
+ {
+ if ($this->hasNext()) {
+ return $this->getCurrentPage() + 1;
+ } else {
+ return null;
+ }
+ }
+
+ public function hasNext()
+ {
+ return $this->getCurrentPage() < $this->getPageCount();
+ }
+
+ public function getQuery()
+ {
+ return $this->query;
+ }
+
+ /**
+ * Returns an array of "local" pages given the page count and current page number
+ *
+ * @return array
+ */
+ protected function getPages()
+ {
+ $page = $this->getPageCount();
+ $current = $this->getCurrentPage();
+
+ $range = [];
+
+ if ($page < 10) {
+ // Show all pages if we have less than 10
+ for ($i = 1; $i < 10; $i++) {
+ if ($i > $page) {
+ break;
+ }
+
+ $range[$i] = $i;
+ }
+ } else {
+ // More than 10 pages:
+ foreach ([1, 2] as $i) {
+ $range[$i] = $i;
+ }
+
+ if ($current < 6) {
+ // We are on page 1-5 from
+ for ($i = 1; $i <= 7; $i++) {
+ $range[$i] = $i;
+ }
+ } else {
+ // Current page > 5
+ $range[] = '…';
+
+ if (($page - $current) < 5) {
+ // Less than 5 pages left
+ $start = 5 - ($page - $current);
+ } else {
+ $start = 1;
+ }
+
+ for ($i = $current - $start; $i < ($current + (4 - $start)); $i++) {
+ if ($i > $page) {
+ break;
+ }
+
+ $range[$i] = $i;
+ }
+ }
+
+ if ($current < ($page - 2)) {
+ $range[] = '…';
+ }
+
+ foreach ([$page - 1, $page] as $i) {
+ $range[$i] = $i;
+ }
+ }
+
+ if (empty($range)) {
+ $range[] = 1;
+ }
+
+ return $range;
+ }
+
+ public function getDefaultItemCountPerPage()
+ {
+ return $this->defaultItemCountPerPage;
+ }
+
+ public function setDefaultItemCountPerPage($count)
+ {
+ $this->defaultItemCountPerPage = (int) $count;
+ return $this;
+ }
+
+ public function setUrl(Url $url)
+ {
+ $page = (int) $url->shift($this->getPageParam());
+ $perPage = (int) $url->getParam($this->getPerPageParam());
+ if ($perPage > 0) {
+ $this->setItemsPerPage($perPage);
+ } else {
+ if (! $this->getQuery()->hasLimit()) {
+ $this->setItemsPerPage($this->getDefaultItemCountPerPage());
+ }
+ }
+ if ($page > 0) {
+ $this->setCurrentPage($page);
+ }
+
+ $this->url = $url;
+
+ return $this;
+ }
+
+ public function getUrl()
+ {
+ if ($this->url === null) {
+ $this->setUrl(Url::fromRequest());
+ }
+
+ return $this->url;
+ }
+
+ public function getPreviousLabel()
+ {
+ return $this->getLabel($this->getCurrentPage() - 1);
+ }
+
+ protected function getNextLabel()
+ {
+ return $this->getLabel($this->getCurrentPage() + 1);
+ }
+
+ protected function getLabel($page)
+ {
+ return sprintf(
+ $this->translate('Show rows %u to %u of %u'),
+ $this->firstRowOnPage($page),
+ $this->lastRowOnPage($page),
+ $this->getTotalItemCount()
+ );
+ }
+
+ protected function renderPrevious()
+ {
+ return Html::tag('li', [
+ 'class' => 'nav-item'
+ ], Link::create(
+ Icon::create('angle-double-left'),
+ $this->makeUrl($this->getPrevious()),
+ null,
+ [
+ 'title' => $this->getPreviousLabel(),
+ 'class' => 'previous-page'
+ ]
+ ));
+ }
+
+ protected function renderNoPrevious()
+ {
+ return $this->renderDisabled(Html::tag('span', [
+ 'class' => 'previous-page'
+ ], [
+ $this->srOnly($this->translate('Previous page')),
+ Icon::create('angle-double-left')
+ ]));
+ }
+
+ protected function renderNext()
+ {
+ return Html::tag('li', [
+ 'class' => 'nav-item'
+ ], Link::create(
+ Icon::create('angle-double-right'),
+ $this->makeUrl($this->getNext()),
+ null,
+ [
+ 'title' => $this->getNextLabel(),
+ 'class' => 'next-page'
+ ]
+ ));
+ }
+
+ protected function renderNoNext()
+ {
+ return $this->renderDisabled(Html::tag('span', [
+ 'class' => 'previous-page'
+ ], [
+ $this->srOnly($this->translate('Next page')),
+ Icon::create('angle-double-right')
+ ]));
+ }
+
+ protected function renderDots()
+ {
+ return $this->renderDisabled(Html::tag('span', null, '…'));
+ }
+
+ protected function renderInnerPages()
+ {
+ $pages = [];
+ $current = $this->getCurrentPage();
+
+ foreach ($this->getPages() as $page) {
+ if ($page === '…') {
+ $pages[] = $this->renderDots();
+ } else {
+ $pages[] = Html::tag(
+ 'li',
+ $page === $current ? ['class' => 'active'] : null,
+ $this->makeLink($page)
+ );
+ }
+ }
+
+ return $pages;
+ }
+
+ protected function lastRowOnPage($page)
+ {
+ $perPage = $this->getItemsPerPage();
+ $total = $this->getTotalItemCount();
+ $last = $page * $perPage;
+ if ($last > $total) {
+ $last = $total;
+ }
+
+ return $last;
+ }
+
+ protected function firstRowOnPage($page)
+ {
+ return ($page - 1) * $this->getItemsPerPage() + 1;
+ }
+
+ protected function makeLink($page)
+ {
+ return Link::create(
+ $page,
+ $this->makeUrl($page),
+ null,
+ ['title' => $this->getLabel($page)]
+ );
+ }
+
+ protected function makeUrl($page)
+ {
+ if ($page) {
+ return $this->getUrl()->with('page', $page);
+ } else {
+ return $this->getUrl();
+ }
+ }
+
+ protected function srOnly($content)
+ {
+ return Html::tag('span', ['class' => 'sr-only'], $content);
+ }
+
+ protected function renderDisabled($content)
+ {
+ return Html::tag('li', [
+ 'class' => ['nav-item', 'disabled'],
+ 'aria-hidden' => 'true'
+ ], $content);
+ }
+
+ protected function renderList()
+ {
+ return Html::tag(
+ 'ul',
+ ['class' => ['nav', 'tab-nav']],
+ [
+ $this->hasPrevious() ? $this->renderPrevious() : $this->renderNoPrevious(),
+ $this->renderInnerPages(),
+ $this->hasNext() ? $this->renderNext() : $this->renderNoNext()
+ ]
+ );
+ }
+
+ public function assemble()
+ {
+ $this->add([
+ $this->renderScreenReaderHeader(),
+ $this->renderList()
+ ]);
+ }
+
+ protected function renderScreenReaderHeader()
+ {
+ return Html::tag('h2', [
+ // 'id' => $this->protectId('pagination') -> why?
+ 'class' => 'sr-only',
+ 'tab-index' => '-1'
+ ], $this->translate('Pagination'));
+ }
+
+ public function render()
+ {
+ if ($this->getPageCount() < 2) {
+ return '';
+ } else {
+ return parent::render();
+ }
+ }
+}
diff --git a/vendor/gipfl/icingaweb2/src/Widget/Tabs.php b/vendor/gipfl/icingaweb2/src/Widget/Tabs.php
new file mode 100644
index 0000000..38bf4cd
--- /dev/null
+++ b/vendor/gipfl/icingaweb2/src/Widget/Tabs.php
@@ -0,0 +1,44 @@
+<?php
+
+namespace gipfl\IcingaWeb2\Widget;
+
+use Exception;
+use Icinga\Web\Widget\Tabs as WebTabs;
+use InvalidArgumentException;
+use ipl\Html\ValidHtml;
+
+class Tabs extends WebTabs implements ValidHtml
+{
+ /**
+ * @param string $name
+ * @return $this
+ */
+ public function activate($name)
+ {
+ try {
+ parent::activate($name);
+ } catch (Exception $e) {
+ throw new InvalidArgumentException(
+ "Can't activate '$name', there is no such tab"
+ );
+ }
+
+ return $this;
+ }
+
+ /**
+ * @param string $name
+ * @param array|\Icinga\Web\Widget\Tab $tab
+ * @return $this
+ */
+ public function add($name, $tab)
+ {
+ try {
+ parent::add($name, $tab);
+ } catch (Exception $e) {
+ throw new InvalidArgumentException($e->getMessage());
+ }
+
+ return $this;
+ }
+}
diff --git a/vendor/gipfl/icingaweb2/src/Zf1/Db/CountQuery.php b/vendor/gipfl/icingaweb2/src/Zf1/Db/CountQuery.php
new file mode 100644
index 0000000..07204b8
--- /dev/null
+++ b/vendor/gipfl/icingaweb2/src/Zf1/Db/CountQuery.php
@@ -0,0 +1,93 @@
+<?php
+
+namespace gipfl\IcingaWeb2\Zf1\Db;
+
+use gipfl\ZfDb\Select;
+use RuntimeException;
+use Zend_Db_Select as ZfSelect;
+
+class CountQuery
+{
+ /** @var Select|ZfSelect */
+ private $query;
+
+ private $maxRows;
+
+ /**
+ * ZfCountQuery constructor.
+ * @param Select|ZfSelect $query
+ */
+ public function __construct($query)
+ {
+ if ($query instanceof Select || $query instanceof ZfSelect) {
+ $this->query = $query;
+ } else {
+ throw new RuntimeException('Got no supported ZF1 Select object');
+ }
+ }
+
+ public function setMaxRows($max)
+ {
+ $this->maxRows = $max;
+ return $this;
+ }
+
+ public function getQuery()
+ {
+ if ($this->needsSubQuery()) {
+ return $this->buildSubQuery();
+ } else {
+ return $this->buildSimpleQuery();
+ }
+ }
+
+ protected function hasOneOf($parts)
+ {
+ foreach ($parts as $part) {
+ if ($this->hasPart($part)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ protected function hasPart($part)
+ {
+ $values = $this->query->getPart($part);
+ return ! empty($values);
+ }
+
+ protected function needsSubQuery()
+ {
+ return null !== $this->maxRows || $this->hasOneOf([
+ Select::GROUP,
+ Select::UNION
+ ]);
+ }
+
+ protected function buildSubQuery()
+ {
+ $sub = clone($this->query);
+ $sub->limit(null, null);
+ $class = $this->query;
+ $query = new $class($this->query->getAdapter());
+ $query->from($sub, ['cnt' => 'COUNT(*)']);
+ if (null !== $this->maxRows) {
+ $sub->limit($this->maxRows + 1);
+ }
+
+ return $query;
+ }
+
+ protected function buildSimpleQuery()
+ {
+ $query = clone($this->query);
+ $query->reset(Select::COLUMNS);
+ $query->reset(Select::ORDER);
+ $query->reset(Select::LIMIT_COUNT);
+ $query->reset(Select::LIMIT_OFFSET);
+ $query->columns(['cnt' => 'COUNT(*)']);
+ return $query;
+ }
+}
diff --git a/vendor/gipfl/icingaweb2/src/Zf1/Db/FilterRenderer.php b/vendor/gipfl/icingaweb2/src/Zf1/Db/FilterRenderer.php
new file mode 100644
index 0000000..b51296f
--- /dev/null
+++ b/vendor/gipfl/icingaweb2/src/Zf1/Db/FilterRenderer.php
@@ -0,0 +1,335 @@
+<?php
+
+namespace gipfl\IcingaWeb2\Zf1\Db;
+
+use gipfl\ZfDb\Adapter\Adapter as Db;
+use gipfl\ZfDb\Exception\SelectException;
+use gipfl\ZfDb\Expr;
+use gipfl\ZfDb\Select;
+use Icinga\Data\Filter\Filter;
+use Icinga\Data\Filter\FilterAnd;
+use Icinga\Data\Filter\FilterChain;
+use Icinga\Data\Filter\FilterExpression;
+use Icinga\Data\Filter\FilterNot;
+use Icinga\Data\Filter\FilterOr;
+use Icinga\Data\SimpleQuery;
+use InvalidArgumentException;
+use RuntimeException;
+use Zend_Db_Adapter_Abstract as DbAdapter;
+use Zend_Db_Expr as DbExpr;
+use Zend_Db_Select as DbSelect;
+use Zend_Db_Select_Exception as DbSelectException;
+
+class FilterRenderer
+{
+ private $db;
+
+ /** @var Filter */
+ private $filter;
+
+ /** @var array */
+ private $columnMap;
+
+ /** @var string */
+ private $dbExprClass;
+
+ /**
+ * FilterRenderer constructor.
+ * @param Filter $filter
+ * @param Db|DbAdapter $db
+ */
+ public function __construct(Filter $filter, $db)
+ {
+ $this->filter = $filter;
+ if ($db instanceof Db) {
+ $this->db = $db;
+ $this->dbExprClass = Expr::class;
+ } elseif ($db instanceof DbAdapter) {
+ $this->db = $db;
+ $this->dbExprClass = DbExpr::class;
+ } else {
+ throw new RuntimeException('Got no supported ZF1 DB adapter');
+ }
+ }
+
+ /**
+ * @return Expr|DbExpr
+ */
+ public function toDbExpression()
+ {
+ return $this->expr($this->render());
+ }
+
+ /**
+ * @return Expr|DbExpr
+ */
+ protected function expr($content)
+ {
+ $class = $this->dbExprClass;
+ return new $class($content);
+ }
+
+ /**
+ * @param Filter $filter
+ * @param Select|DbSelect|SimpleQuery $query
+ * @return Select|DbSelect|SimpleQuery
+ */
+ public static function applyToQuery(Filter $filter, $query)
+ {
+ if ($query instanceof SimpleQuery) {
+ $query->applyFilter($filter);
+ return $query;
+ }
+ if (! ($query instanceof Select || $query instanceof DbSelect)) {
+ throw new RuntimeException('Got no supported ZF1 Select object');
+ }
+
+ if (! $filter->isEmpty()) {
+ $renderer = new static($filter, $query->getAdapter());
+ $renderer->extractColumnMap($query);
+ $query->where($renderer->toDbExpression());
+ }
+
+ return $query;
+ }
+
+ protected function lookupColumnAlias($column)
+ {
+ if (array_key_exists($column, $this->columnMap)) {
+ return $this->columnMap[$column];
+ } else {
+ return $column;
+ }
+ }
+
+ protected function extractColumnMap($query)
+ {
+ $map = [];
+ try {
+ $columns = $query->getPart(Select::COLUMNS);
+ } catch (SelectException $e) {
+ // Will not happen.
+ throw new RuntimeException($e->getMessage());
+ } catch (DbSelectException $e) {
+ // Will not happen.
+ throw new RuntimeException($e->getMessage());
+ }
+
+ foreach ($columns as $col) {
+ if ($col[1] instanceof Expr || $col[1] instanceof DbExpr) {
+ $map[$col[2]] = (string) $col[1];
+ $map[$col[2]] = $col[1];
+ } else {
+ $map[$col[2]] = $col[0] . '.' . $col[1];
+ }
+ }
+
+ $this->columnMap = $map;
+ }
+
+ /**
+ * @return string
+ */
+ public function render()
+ {
+ return $this->renderFilter($this->filter);
+ }
+
+ protected function renderFilterChain(FilterChain $filter, $level = 0)
+ {
+ $prefix = '';
+
+ if ($filter instanceof FilterAnd) {
+ $op = ' AND ';
+ } elseif ($filter instanceof FilterOr) {
+ $op = ' OR ';
+ } elseif ($filter instanceof FilterNot) {
+ $op = ' AND ';
+ $prefix = 'NOT ';
+ } else {
+ throw new InvalidArgumentException(
+ 'Cannot render a %s filter chain for Zf Db',
+ get_class($filter)
+ );
+ }
+
+ $parts = [];
+ if ($filter->isEmpty()) {
+ // Hint: we might want to fail here
+ return '';
+ } else {
+ foreach ($filter->filters() as $f) {
+ $part = $this->renderFilter($f, $level + 1);
+ if ($part !== '') {
+ $parts[] = $part;
+ }
+ }
+ if (empty($parts)) {
+ // will not happen, as we are not empty
+ return '';
+ } else {
+ if ($level > 0) {
+ return "$prefix (" . implode($op, $parts) . ')';
+ } else {
+ return $prefix . implode($op, $parts);
+ }
+ }
+ }
+ }
+
+ protected function renderFilterExpression(FilterExpression $filter)
+ {
+ $col = $this->lookupColumnAlias($filter->getColumn());
+ if (! $col instanceof Expr && ! $col instanceof DbExpr && ! ctype_digit($col)) {
+ $col = $this->db->quoteIdentifier($col);
+ }
+ $sign = $filter->getSign();
+ $expression = $filter->getExpression();
+
+ if (is_array($expression)) {
+ return $this->renderArrayExpression($col, $sign, $expression);
+ }
+
+ if ($sign === '=') {
+ if (strpos($expression, '*') === false) {
+ return $this->renderAny($col, $sign, $expression);
+ } else {
+ return $this->renderLike($col, $expression);
+ }
+ }
+
+ if ($sign === '!=') {
+ if (strpos($expression, '*') === false) {
+ return $this->renderAny($col, $sign, $expression);
+ } else {
+ return $this->renderNotLike($col, $expression);
+ }
+ }
+
+ return $this->renderAny($col, $sign, $expression);
+ }
+
+
+ protected function renderLike($col, $expression)
+ {
+ if ($expression === '*') {
+ return $this->expr('TRUE');
+ }
+
+ return $col . ' LIKE ' . $this->escape($this->escapeWildcards($expression));
+ }
+
+ protected function renderNotLike($col, $expression)
+ {
+ if ($expression === '*') {
+ return $this->expr('FALSE');
+ }
+
+ return sprintf(
+ '(%1$s NOT LIKE %2$s OR %1$s IS NULL)',
+ $col,
+ $this->escape($this->escapeWildcards($expression))
+ );
+ }
+
+ protected function renderNotEqual($col, $expression)
+ {
+ return sprintf('(%1$s != %2$s OR %1$s IS NULL)', $col, $this->escape($expression));
+ }
+
+ protected function renderAny($col, $sign, $expression)
+ {
+ return sprintf('%s %s %s', $col, $sign, $this->escape($expression));
+ }
+
+ protected function renderArrayExpression($col, $sign, $expression)
+ {
+ if ($sign === '=') {
+ return $col . ' IN (' . $this->escape($expression) . ')';
+ } elseif ($sign === '!=') {
+ return sprintf(
+ '(%1$s NOT IN (%2$s) OR %1$s IS NULL)',
+ $col,
+ $this->escape($expression)
+ );
+ }
+
+ throw new InvalidArgumentException(
+ 'Array expressions can only be rendered for = and !=, got %s',
+ $sign
+ );
+ }
+
+ /**
+ * @param Filter $filter
+ * @param int $level
+ * @return string|Expr|DbExpr
+ */
+ protected function renderFilter(Filter $filter, $level = 0)
+ {
+ if ($filter instanceof FilterChain) {
+ return $this->renderFilterChain($filter, $level);
+ } elseif ($filter instanceof FilterExpression) {
+ return $this->renderFilterExpression($filter);
+ } else {
+ throw new RuntimeException(sprintf(
+ 'Filter of type FilterChain or FilterExpression expected, got %s',
+ get_class($filter)
+ ));
+ }
+ }
+
+ protected function escape($value)
+ {
+ // bindParam? bindValue?
+ if (is_array($value)) {
+ $ret = [];
+ foreach ($value as $val) {
+ $ret[] = $this->escape($val);
+ }
+ return implode(', ', $ret);
+ } else {
+ return $this->db->quote($value);
+ }
+ }
+
+ protected function escapeWildcards($value)
+ {
+ return preg_replace('/\*/', '%', $value);
+ }
+
+ public function whereToSql($col, $sign, $expression)
+ {
+ if (is_array($expression)) {
+ if ($sign === '=') {
+ return $col . ' IN (' . $this->escape($expression) . ')';
+ } elseif ($sign === '!=') {
+ return sprintf('(%1$s NOT IN (%2$s) OR %1$s IS NULL)', $col, $this->escape($expression));
+ }
+
+ throw new InvalidArgumentException(
+ 'Unable to render array expressions with operators other than equal or not equal'
+ );
+ } elseif ($sign === '=' && strpos($expression, '*') !== false) {
+ if ($expression === '*') {
+ return $this->expr('TRUE');
+ }
+
+ return $col . ' LIKE ' . $this->escape($this->escapeWildcards($expression));
+ } elseif ($sign === '!=' && strpos($expression, '*') !== false) {
+ if ($expression === '*') {
+ return $this->expr('FALSE');
+ }
+
+ return sprintf(
+ '(%1$s NOT LIKE %2$s OR %1$s IS NULL)',
+ $col,
+ $this->escape($this->escapeWildcards($expression))
+ );
+ } elseif ($sign === '!=') {
+ return sprintf('(%1$s %2$s %3$s OR %1$s IS NULL)', $col, $sign, $this->escape($expression));
+ } else {
+ return sprintf('%s %s %s', $col, $sign, $this->escape($expression));
+ }
+ }
+}
diff --git a/vendor/gipfl/icingaweb2/src/Zf1/Db/SelectPaginationAdapter.php b/vendor/gipfl/icingaweb2/src/Zf1/Db/SelectPaginationAdapter.php
new file mode 100644
index 0000000..599a3ee
--- /dev/null
+++ b/vendor/gipfl/icingaweb2/src/Zf1/Db/SelectPaginationAdapter.php
@@ -0,0 +1,111 @@
+<?php
+
+namespace gipfl\IcingaWeb2\Zf1\Db;
+
+use gipfl\IcingaWeb2\Data\Paginatable;
+use gipfl\ZfDb\Select;
+use gipfl\ZfDb\Exception\SelectException;
+use Icinga\Application\Benchmark;
+use RuntimeException;
+use Zend_Db_Select as ZfSelect;
+use Zend_Db_Select_Exception as ZfDbSelectException;
+
+class SelectPaginationAdapter implements Paginatable
+{
+ private $query;
+
+ private $countQuery;
+
+ private $cachedCount;
+
+ private $cachedCountQuery;
+
+ public function __construct($query)
+ {
+ if ($query instanceof Select || $query instanceof ZfSelect) {
+ $this->query = $query;
+ } else {
+ throw new RuntimeException('Got no supported ZF1 Select object');
+ }
+ }
+
+ public function getCountQuery()
+ {
+ if ($this->countQuery === null) {
+ $this->countQuery = (new CountQuery($this->query))->getQuery();
+ }
+
+ return $this->countQuery;
+ }
+
+ #[\ReturnTypeWillChange]
+ public function count()
+ {
+ $queryString = (string) $this->getCountQuery();
+ if ($this->cachedCountQuery !== $queryString) {
+ Benchmark::measure('Running count() for pagination');
+ $this->cachedCountQuery = $queryString;
+ $count = $this->query->getAdapter()->fetchOne(
+ $queryString
+ );
+ $this->cachedCount = $count;
+ Benchmark::measure("Counted $count rows");
+ }
+
+ return $this->cachedCount;
+ }
+
+ public function limit($count = null, $offset = null)
+ {
+ $this->query->limit($count, $offset);
+ }
+
+ public function hasLimit()
+ {
+ return $this->getLimit() !== null;
+ }
+
+ public function getLimit()
+ {
+ return $this->getQueryPart(Select::LIMIT_COUNT);
+ }
+
+ public function setLimit($limit)
+ {
+ $this->query->limit(
+ $limit,
+ $this->getOffset()
+ );
+ }
+
+ public function hasOffset()
+ {
+ return $this->getOffset() !== null;
+ }
+
+ public function getOffset()
+ {
+ return $this->getQueryPart(Select::LIMIT_OFFSET);
+ }
+
+ protected function getQueryPart($part)
+ {
+ try {
+ return $this->query->getPart($part);
+ } catch (SelectException $e) {
+ // Will not happen if $part is correct.
+ throw new RuntimeException($e);
+ } catch (ZfDbSelectException $e) {
+ // Will not happen if $part is correct.
+ throw new RuntimeException($e);
+ }
+ }
+
+ public function setOffset($offset)
+ {
+ $this->query->limit(
+ $this->getLimit(),
+ $offset
+ );
+ }
+}
diff --git a/vendor/gipfl/icingaweb2/src/Zf1/SimpleViewRenderer.php b/vendor/gipfl/icingaweb2/src/Zf1/SimpleViewRenderer.php
new file mode 100644
index 0000000..89b36a4
--- /dev/null
+++ b/vendor/gipfl/icingaweb2/src/Zf1/SimpleViewRenderer.php
@@ -0,0 +1,115 @@
+<?php
+
+namespace gipfl\IcingaWeb2\Zf1;
+
+use gipfl\IcingaWeb2\Widget\Content;
+use gipfl\IcingaWeb2\Widget\Controls;
+use ipl\Html\Error;
+use Icinga\Application\Icinga;
+use ipl\Html\ValidHtml;
+use Zend_Controller_Action_Helper_Abstract as Helper;
+use Zend_Controller_Action_HelperBroker as HelperBroker;
+
+class SimpleViewRenderer extends Helper implements ValidHtml
+{
+ private $disabled = false;
+
+ private $rendered = false;
+
+ /** @var \Zend_View_Interface */
+ public $view;
+
+ public function init()
+ {
+ // Register view with action controller (unless already registered)
+ if ((null !== $this->_actionController) && (null === $this->_actionController->view)) {
+ $this->_actionController->view = $this->view;
+ }
+ }
+
+ public function disable($disabled = true)
+ {
+ $this->disabled = $disabled;
+ return $this;
+ }
+
+ public function replaceZendViewRenderer()
+ {
+ /** @var \Zend_Controller_Action_Helper_ViewRenderer $viewRenderer */
+ $viewRenderer = Icinga::app()->getViewRenderer();
+ $viewRenderer->setNeverRender();
+ $viewRenderer->setNeverController();
+ HelperBroker::removeHelper('viewRenderer');
+ HelperBroker::addHelper($this);
+ $this->view = $viewRenderer->view;
+ return $this;
+ }
+
+ public function render($action = null, $name = null, $noController = null)
+ {
+ if (null === $name) {
+ $name = null; // $this->getResponseSegment();
+ }
+ // Compat.
+ if (isset($this->_actionController)
+ && get_class($this->_actionController) === 'Icinga\\Controllers\\ErrorController'
+ ) {
+ $html = $this->simulateErrorController();
+ } else {
+ $html = '';
+ if (null !== $this->view->controls) {
+ $html .= $this->view->controls->__toString();
+ }
+
+ if (null !== $this->view->content) {
+ $html .= $this->view->content->__toString();
+ }
+ }
+
+ $this->getResponse()->appendBody($html, $name);
+ // $this->setNoRender();
+ $this->rendered = true;
+ }
+
+ protected function simulateErrorController()
+ {
+ $errorHandler = $this->_actionController->getParam('error_handler');
+ if (isset($errorHandler->exception)) {
+ $error = Error::show($errorHandler->exception);
+ } else {
+ $error = 'An unknown error occured';
+ }
+
+ /** @var \Icinga\Web\Request $request */
+ $request = $this->getRequest();
+ $controls = new Controls();
+ $controls->getTabs()->add('error', [
+ 'label' => t('Error'),
+ 'url' => $request->getUrl(),
+ ])->activate('error');
+ $content = new Content();
+ $content->add($error);
+
+ return $controls . $content;
+ }
+
+ public function shouldRender()
+ {
+ return ! $this->disabled && ! $this->rendered;
+ }
+
+ public function postDispatch()
+ {
+ if ($this->shouldRender()) {
+ $this->render();
+ }
+ }
+
+ public function getName()
+ {
+ // TODO: This is wrong, should be 'viewRenderer' - but that would
+ // currently break nearly everything, starting with full layout
+ // rendering
+ return 'ViewRenderer';
+ }
+}
diff --git a/vendor/gipfl/influxdb/LICENSE b/vendor/gipfl/influxdb/LICENSE
new file mode 100644
index 0000000..dd88e09
--- /dev/null
+++ b/vendor/gipfl/influxdb/LICENSE
@@ -0,0 +1,21 @@
+The MIT License
+
+Copyright (c) 2018 Thomas Gelf
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/vendor/gipfl/influxdb/composer.json b/vendor/gipfl/influxdb/composer.json
new file mode 100644
index 0000000..8c9aec4
--- /dev/null
+++ b/vendor/gipfl/influxdb/composer.json
@@ -0,0 +1,25 @@
+{
+ "name": "gipfl/influxdb",
+ "description": "InfluxDB client library",
+ "type": "library",
+ "license": "MIT",
+ "autoload": {
+ "psr-4": {
+ "gipfl\\InfluxDb\\": "src/"
+ }
+ },
+ "authors": [
+ {
+ "name": "Thomas Gelf",
+ "email": "thomas@gelf.net"
+ }
+ ],
+ "require": {
+ "php": ">=5.6.0",
+ "ext-ctype": "*",
+ "ext-pcntl": "*",
+ "gipfl/curl": ">=0.1.1",
+ "react/event-loop": ">=1.1",
+ "gipfl/json": ">=0.2"
+ }
+}
diff --git a/vendor/gipfl/influxdb/src/ChunkedInfluxDbWriter.php b/vendor/gipfl/influxdb/src/ChunkedInfluxDbWriter.php
new file mode 100644
index 0000000..37473a7
--- /dev/null
+++ b/vendor/gipfl/influxdb/src/ChunkedInfluxDbWriter.php
@@ -0,0 +1,158 @@
+<?php
+
+namespace gipfl\InfluxDb;
+
+use gipfl\Curl\RequestError;
+use Psr\Http\Message\ResponseInterface;
+use Psr\Log\LoggerAwareInterface;
+use Psr\Log\LoggerAwareTrait;
+use Psr\Log\NullLogger;
+use React\EventLoop\LoopInterface;
+use React\EventLoop\TimerInterface;
+
+/**
+ * Gives no result, enqueue and forget
+ */
+class ChunkedInfluxDbWriter implements LoggerAwareInterface
+{
+ use LoggerAwareTrait;
+
+ const DEFAULT_BUFFER_SIZE = 5000;
+
+ const DEFAULT_FLUSH_INTERVAL = 0.2;
+
+ const DEFAULT_PRECISION = 's';
+
+ /** @var int */
+ protected $bufferSize = self::DEFAULT_BUFFER_SIZE;
+
+ /** @var float */
+ protected $flushInterval = self::DEFAULT_FLUSH_INTERVAL;
+
+ /** @var string */
+ protected $precision = self::DEFAULT_PRECISION;
+
+ /** @var DataPoint[] */
+ protected $buffer = [];
+
+ /** @var InfluxDbConnection */
+ protected $connection;
+
+ /** @var string */
+ protected $dbName;
+
+ /** @var LoopInterface */
+ protected $loop;
+
+ /** @var ?TimerInterface */
+ protected $flushTimer;
+
+ public function __construct(InfluxDbConnection $connection, $dbName, LoopInterface $loop)
+ {
+ $this->setLogger(new NullLogger());
+ $this->connection = $connection;
+ $this->dbName = $dbName;
+ $this->loop = $loop;
+ }
+
+ /**
+ * @param DataPoint $point
+ */
+ public function enqueue(DataPoint $point)
+ {
+ $this->buffer[] = $point;
+ $count = count($this->buffer);
+ if ($count >= $this->bufferSize) {
+ $this->flush();
+ } else {
+ $this->startFlushTimer();
+ }
+ }
+
+ /**
+ * @param int $bufferSize
+ * @return ChunkedInfluxDbWriter
+ */
+ public function setBufferSize($bufferSize)
+ {
+ $this->bufferSize = $bufferSize;
+ return $this;
+ }
+
+ /**
+ * @param float $flushInterval
+ * @return ChunkedInfluxDbWriter
+ */
+ public function setFlushInterval($flushInterval)
+ {
+ $this->flushInterval = $flushInterval;
+ return $this;
+ }
+
+ /**
+ * @param string $precision ns,u,ms,s,m,h
+ * @return ChunkedInfluxDbWriter
+ */
+ public function setPrecision($precision)
+ {
+ $this->precision = $precision;
+ return $this;
+ }
+
+ public function flush()
+ {
+ $buffer = $this->buffer;
+ $this->buffer = [];
+ $this->stopFlushTimer();
+ $this->logger->debug(sprintf('Flushing InfluxDB buffer, sending %d data points', count($buffer)));
+ $start = microtime(true);
+ $this->connection->writeDataPoints($this->dbName, $buffer, $this->precision)
+ ->then(function (ResponseInterface $response) use ($start) {
+ $code = $response->getStatusCode();
+ $duration = (microtime(true) - $start) * 1000;
+ if ($code > 199 && $code < 300) {
+ $this->logger->debug(sprintf('Got response from InfluxDB after %.2Fms', $duration));
+ } else {
+ $this->logger->error(sprintf(
+ 'Got unexpected %d from InfluxDB after %.2Fms: %s',
+ $code,
+ $duration,
+ $response->getReasonPhrase()
+ ));
+ }
+ }, function (RequestError $e) {
+ $this->logger->error($e->getMessage());
+ })->done();
+ }
+
+ public function stop()
+ {
+ $this->flush();
+ }
+
+ protected function startFlushTimer()
+ {
+ if ($this->flushTimer === null) {
+ $this->flushTimer = $this->loop->addPeriodicTimer($this->flushInterval, function () {
+ if (! empty($this->buffer)) {
+ $this->flush();
+ }
+ });
+ }
+ }
+
+ protected function stopFlushTimer()
+ {
+ if ($this->flushTimer) {
+ $this->loop->cancelTimer($this->flushTimer);
+ $this->flushTimer = null;
+ }
+ }
+
+ public function __destruct()
+ {
+ $this->stopFlushTimer();
+ $this->loop = null;
+ $this->connection = null;
+ }
+}
diff --git a/vendor/gipfl/influxdb/src/DataPoint.php b/vendor/gipfl/influxdb/src/DataPoint.php
new file mode 100644
index 0000000..f272206
--- /dev/null
+++ b/vendor/gipfl/influxdb/src/DataPoint.php
@@ -0,0 +1,63 @@
+<?php
+
+namespace gipfl\InfluxDb;
+
+use InvalidArgumentException;
+use function array_key_exists;
+use function array_merge;
+use function is_array;
+use function is_object;
+use function ksort;
+
+class DataPoint
+{
+ protected $timestamp;
+
+ protected $measurement;
+
+ protected $tags = [];
+
+ protected $fields;
+
+ public function __construct($measurement, $tags = [], $fields = [], $timestamp = null)
+ {
+ $this->measurement = (string) $measurement;
+ if ($timestamp !== null) {
+ $this->timestamp = $timestamp;
+ }
+
+ if (! empty($tags)) {
+ $this->addTags($tags);
+ }
+
+ if (is_array($fields) || is_object($fields)) {
+ $this->fields = (array) $fields;
+ } else {
+ $this->fields = ['value' => $fields];
+ }
+
+ if (empty($this->fields)) {
+ throw new InvalidArgumentException('At least one field/value is required');
+ }
+ }
+
+ public function addTags($tags)
+ {
+ $this->tags = array_merge($this->tags, (array) $tags);
+ ksort($this->tags);
+ }
+
+ public function getTag($name, $default = null)
+ {
+ if (array_key_exists($name, $this->tags)) {
+ return $this->tags[$name];
+ } else {
+ return $default;
+ }
+ }
+
+ public function __toString()
+ {
+ return LineProtocol::renderMeasurement($this->measurement, $this->tags, $this->fields, $this->timestamp);
+ }
+}
diff --git a/vendor/gipfl/influxdb/src/Escape.php b/vendor/gipfl/influxdb/src/Escape.php
new file mode 100644
index 0000000..e6cb555
--- /dev/null
+++ b/vendor/gipfl/influxdb/src/Escape.php
@@ -0,0 +1,67 @@
+<?php
+
+namespace gipfl\InfluxDb;
+
+use InvalidArgumentException;
+use function addcslashes;
+use function ctype_digit;
+use function is_bool;
+use function is_int;
+use function is_null;
+use function preg_match;
+use function strpos;
+
+abstract class Escape
+{
+ const ESCAPE_COMMA_SPACE = ' ,\\';
+
+ const ESCAPE_COMMA_EQUAL_SPACE = ' =,\\';
+
+ const ESCAPE_DOUBLE_QUOTES = '"\\';
+
+ const NULL = 'null';
+
+ const TRUE = 'true';
+
+ const FALSE = 'false';
+
+ public static function measurement($value)
+ {
+ static::assertNoNewline($value);
+ return addcslashes($value, self::ESCAPE_COMMA_SPACE);
+ }
+
+ public static function key($value)
+ {
+ static::assertNoNewline($value);
+ return addcslashes($value, self::ESCAPE_COMMA_EQUAL_SPACE);
+ }
+
+ public static function tagValue($value)
+ {
+ static::assertNoNewline($value);
+ return addcslashes($value, self::ESCAPE_COMMA_EQUAL_SPACE);
+ }
+
+ public static function fieldValue($value)
+ {
+ // Faster checks first
+ if (is_int($value) || ctype_digit($value) || preg_match('/^-\d+$/', $value)) {
+ return "{$value}i";
+ } elseif (is_bool($value)) {
+ return $value ? self::TRUE : self::FALSE;
+ } elseif (is_null($value)) {
+ return self::NULL;
+ } else {
+ static::assertNoNewline($value);
+ return '"' . addcslashes($value, self::ESCAPE_DOUBLE_QUOTES) . '"';
+ }
+ }
+
+ protected static function assertNoNewline($value)
+ {
+ if (strpos($value, "\n") !== false) {
+ throw new InvalidArgumentException('Newlines are forbidden in InfluxDB line protocol');
+ }
+ }
+}
diff --git a/vendor/gipfl/influxdb/src/InfluxDbConnection.php b/vendor/gipfl/influxdb/src/InfluxDbConnection.php
new file mode 100644
index 0000000..d20944a
--- /dev/null
+++ b/vendor/gipfl/influxdb/src/InfluxDbConnection.php
@@ -0,0 +1,24 @@
+<?php
+
+namespace gipfl\InfluxDb;
+
+interface InfluxDbConnection
+{
+ public function ping($verbose = false);
+
+ public function getVersion();
+
+ public function listDatabases();
+
+ public function createDatabase($name);
+
+ public function getHealth();
+
+ /**
+ * @param string $dbName
+ * @param DataPoint[] $dataPoints
+ * @param string|null $precision ns,u,ms,s,m,h
+ * @return \React\Promise\Promise
+ */
+ public function writeDataPoints($dbName, array $dataPoints, $precision = null);
+}
diff --git a/vendor/gipfl/influxdb/src/InfluxDbConnectionFactory.php b/vendor/gipfl/influxdb/src/InfluxDbConnectionFactory.php
new file mode 100644
index 0000000..f260010
--- /dev/null
+++ b/vendor/gipfl/influxdb/src/InfluxDbConnectionFactory.php
@@ -0,0 +1,38 @@
+<?php
+
+namespace gipfl\InfluxDb;
+
+use gipfl\Curl\CurlAsync;
+use React\EventLoop\LoopInterface;
+use React\Promise\Promise;
+use RuntimeException;
+
+abstract class InfluxDbConnectionFactory
+{
+ /**
+ * AsyncInfluxDbWriter constructor.
+ * @param LoopInterface $loop
+ * @param $baseUrl string InfluxDB base URL
+ * @param string|null $username
+ * @param string|null $password
+ * @return Promise <InfluxDbConnection>
+ */
+ public static function create(CurlAsync $curl, $baseUrl, $username = null, $password = null)
+ {
+ $v1 = new InfluxDbConnectionV1($curl, $baseUrl);
+ return $v1->getVersion()->then(function ($version) use ($baseUrl, $username, $password, $curl, $v1) {
+ if ($version === null || preg_match('/^v?2\./', $version)) {
+ $v2 = new InfluxDbConnectionV2($curl, $baseUrl, $username, $password);
+ return $v2->getVersion()->then(function ($version) use ($v2) {
+ if ($version === null) {
+ throw new RuntimeException('Unable to detect InfluxDb version');
+ } else {
+ return $v2;
+ }
+ });
+ } else {
+ return $v1;
+ }
+ });
+ }
+}
diff --git a/vendor/gipfl/influxdb/src/InfluxDbConnectionV1.php b/vendor/gipfl/influxdb/src/InfluxDbConnectionV1.php
new file mode 100644
index 0000000..0b674c2
--- /dev/null
+++ b/vendor/gipfl/influxdb/src/InfluxDbConnectionV1.php
@@ -0,0 +1,311 @@
+<?php
+
+namespace gipfl\InfluxDb;
+
+use gipfl\Curl\CurlAsync;
+use gipfl\Json\JsonString;
+use Psr\Http\Message\ResponseInterface;
+use Ramsey\Uuid\Uuid;
+use React\Promise\Promise;
+use function React\Promise\resolve;
+
+class InfluxDbConnectionV1 implements InfluxDbConnection
+{
+ const API_VERSION = 'v1';
+
+ const USER_AGENT = 'gipfl-InfluxDB/0.5';
+
+ /** @var string */
+ protected $baseUrl;
+
+ protected $version;
+
+ /** @var string|null */
+ protected $username;
+
+ /** @var string|null */
+ protected $password;
+
+ protected $curl;
+
+ /**
+ * AsyncInfluxDbWriter constructor.
+ * @param CurlAsync $curl
+ * @param string $baseUrl InfluxDB base URL
+ * @param ?string $username
+ * @param ?string $password
+ */
+ public function __construct(CurlAsync $curl, $baseUrl, $username = null, $password = null)
+ {
+ $this->baseUrl = rtrim($baseUrl, '/');
+ $this->curl = $curl;
+ $this->setUsername($username);
+ $this->setPassword($password);
+ }
+
+ /**
+ * @param string|null $username
+ * @return $this
+ */
+ public function setUsername($username)
+ {
+ $this->username = $username;
+ return $this;
+ }
+
+ /**
+ * @param string|null $password
+ * @return $this
+ */
+ public function setPassword($password)