diff --git a/AdminUi/Component/EzInfoTwigComponent.php b/AdminUi/Component/EzInfoTwigComponent.php new file mode 100644 index 00000000..9b465f92 --- /dev/null +++ b/AdminUi/Component/EzInfoTwigComponent.php @@ -0,0 +1,84 @@ +<?php + +/** + * @copyright Copyright (C) eZ Systems AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace EzSystems\EzSupportToolsBundle\AdminUi\Component; + +use EzSystems\EzPlatformAdminUi\Component\Renderable; +use EzSystems\EzSupportToolsBundle\SystemInfo\Value\EzSystemInfo; +use Twig\Environment; + +class EzInfoTwigComponent implements Renderable +{ + /** @var string */ + protected $template; + + /** @var \Twig\Environment */ + protected $twig; + + /** @var array */ + protected $parameters; + + /** @var \EzSystems\EzSupportToolsBundle\SystemInfo\Value\EzSystemInfo */ + private $ezSystemInfo; + + /** @var array */ + private $urlList; + + /** + * @param \Twig\Environment $twig + * @param string $template + * @param \EzSystems\EzSupportToolsBundle\SystemInfo\Value\EzSystemInfo $ezSystemInfo + * @param array $urlList + * @param array $parameters + */ + public function __construct( + Environment $twig, + string $template, + EzSystemInfo $ezSystemInfo, + array $urlList, + array $parameters = [] + ) { + $this->twig = $twig; + $this->template = $template; + $this->parameters = $parameters; + $this->ezSystemInfo = $ezSystemInfo; + $this->urlList = $urlList; + } + + /** + * @param array $parameters + * + * @return string + */ + public function render(array $parameters = []): string + { + $urls = $this->replaceUrlPlaceholders(); + + return $this->twig->render( + $this->template, + $parameters + ['urls' => $urls, 'ez' => $this->ezSystemInfo] + $this->parameters + ); + } + + /** + * @return array + */ + private function replaceUrlPlaceholders(): array + { + $urls = $this->urlList; + foreach ($this->urlList as $urlName => $url) { + foreach ($this->ezSystemInfo as $attribute => $value) { + if (is_string($value) && strpos($url,'{ez.' . $attribute . '}') !== false) { + $urls[$urlName] = str_replace('{ez.' . $attribute . '}', $value, $url); + } + } + } + + return $urls; + } +} diff --git a/Resources/config/services.yml b/Resources/config/services.yml index 0d276c2c..951ddbf2 100644 --- a/Resources/config/services.yml +++ b/Resources/config/services.yml @@ -8,10 +8,25 @@ parameters: support_tools.system_info.ezc.wrapper.class: EzSystems\EzSupportToolsBundle\SystemInfo\EzcSystemInfoWrapper support_tools.system_info.collector.composer.lock_file.class: EzSystems\EzSupportToolsBundle\SystemInfo\Collector\JsonComposerLockSystemInfoCollector support_tools.system_info.collector.database.doctrine.class: EzSystems\EzSupportToolsBundle\SystemInfo\Collector\DoctrineDatabaseSystemInfoCollector + support_tools.system_info.collector.system.ez.class: EzSystems\EzSupportToolsBundle\SystemInfo\Collector\EzSystemInfoCollector support_tools.system_info.collector.hardware.ezc.class: EzSystems\EzSupportToolsBundle\SystemInfo\Collector\EzcHardwareSystemInfoCollector support_tools.system_info.collector.php.ezc.class: EzSystems\EzSupportToolsBundle\SystemInfo\Collector\EzcPhpSystemInfoCollector support_tools.system_info.collector.symfony.kernel.config.class: EzSystems\EzSupportToolsBundle\SystemInfo\Collector\ConfigurationSymfonyKernelSystemInfoCollector support_tools.system_info.output_format.json.class: EzSystems\EzSupportToolsBundle\SystemInfo\OutputFormat\JsonOutputFormat + support_tools.ez_url_list: + contact: "https://ez.no/About-eZ/Contact-Us" + license: "https://ez.no/About-our-Software/Licenses-and-agreements" + ttl: "https://ez.no/About-our-Software/Licenses-and-agreements/eZ-Trial-and-Test-License-Agreement-eZ-TTL-v2.1" + service_life: "https://support.ez.no/Public/Service-Life" + support_service: "https://ez.no/Services/Support-Maintenance" + training_service: "https://ez.no/Services/Training" + consulting_service: "https://ez.no/Services/Consulting" + ee_product: "https://ez.no/Products/eZ-Platform-Enterprise-Edition" + install_ee: "https://doc.ezplatform.com/en/#{ez.release}/getting_started/install_ez_enterprise/" + doc: "https://doc.ezplatform.com" + update: "https://doc.ezplatform.com/en/latest/releases/updating_ez_platform/" + gpl_faq: "https://www.gnu.org/licenses/old-licenses/gpl-2.0-faq.en.html#GPLModuleLicense" + support: "https://support.ez.no" services: support_tools.command.dump_info: @@ -34,6 +49,15 @@ services: # SystemInfoCollectors + support_tools.system_info.collector.system.ez: + class: "%support_tools.system_info.collector.system.ez.class%" + arguments: + - "@support_tools.system_info.collector.composer.lock_file" + - "%kernel.debug%" + # Can't tag this before v0.3 (2.5?) as it will blow up in admin UI for missing templates there + # And it does not look like there is anway to add it from this package, so maybe it needs to be made extensible(?) + #tags: [{ name: "support_tools.system_info.collector", identifier: "ez" }] + support_tools.system_info.collector.composer.lock_file: class: "%support_tools.system_info.collector.composer.lock_file.class%" arguments: @@ -77,3 +101,13 @@ services: class: "%support_tools.system_info.output_format.json.class%" tags: - { name: "support_tools.system_info.output_format", format: "json" } + + # Dashboard + EzSystems\EzSupportToolsBundle\AdminUi\Component\EzInfoTwigComponent: + autowire: true + arguments: + $template: '@@ezdesign/dashboard/block/ez.html.twig' + $ezSystemInfo: "@=service('support_tools.system_info.collector.system.ez').collect()" + $urlList: '%support_tools.ez_url_list%' + tags: + - { name: ezplatform.admin_ui.component, group: 'dashboard-blocks', priority: 200 } diff --git a/Resources/translations/dashboard.en.xlf b/Resources/translations/dashboard.en.xlf new file mode 100644 index 00000000..b0c8d8ae --- /dev/null +++ b/Resources/translations/dashboard.en.xlf @@ -0,0 +1,105 @@ +<?xml version="1.0" encoding="utf-8"?> +<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:jms="urn:jms:translation" version="1.2"> + <file source-language="en" target-language="en" datatype="plaintext" original="not.available"> + <header> + <tool tool-id="JMSTranslationBundle" tool-name="JMSTranslationBundle" tool-version="1.1.0-DEV"/> + <note>The source node in most cases contains the sample message as written by the developer. If it looks like a dot-delimitted string such as "form.label.firstname", then the developer has not provided a default message.</note> + </header> + <body> + <trans-unit id="1710718593a511e351e6da36e3bb280e532bb768" resname="dashboard.ez_version.community_end_of_maintenance"> + <source><![CDATA[Unfortunately %release% open source version has reached end of life, <a target="_blank" href="%update_url%">please upgrade</a>.]]></source> + <target state="new"><![CDATA[Unfortunately %release% open source version has reached end of life, <a target="_blank" href="%update_url%">please upgrade</a>.]]></target> + <note>key: dashboard.ez_version.community_end_of_maintenance</note> + </trans-unit> + <trans-unit id="c0d043ecd29c0bdfa32e8bb7faee57111d5ddaf7" resname="dashboard.ez_version.community_end_of_maintenance_upgrade"> + <source><![CDATA[Tip: If you upgrade to eZ Platform Enterprise you'll get access to: + <a target="_blank" href="%license_url%">A business friendly license</a>, + <a target="_blank" href="%ee_product_url%">several productivity features</a>, + <a target="_blank" href="%support_service_url%">professional Support</a> and + <a target="_blank" href="%service_life_url%">longer maintenance periode of your release</a>.]]></source> + <target state="new"><![CDATA[Tip: If you upgrade to eZ Platform Enterprise you'll get access to: + <a target="_blank" href="%license_url%">A business friendly license</a>, + <a target="_blank" href="%ee_product_url%">several productivity features</a>, + <a target="_blank" href="%support_service_url%">professional Support</a> and + <a target="_blank" href="%service_life_url%">longer maintenance periode of your release</a>.]]></target> + <note>key: dashboard.ez_version.community_end_of_maintenance_upgrade</note> + </trans-unit> + <trans-unit id="7eb6ab2973439efa8ff820310952e02bc81209cb" resname="dashboard.ez_version.community_severity_non"> + <source><![CDATA[Welcome to the open source %release% release. Using the community friendly <a target="_blank" href="%license_url%">GPL license</a>, + <a target="_blank" href="%gpl_faq_url%">sharing your code</a> is what it's all about.]]></source> + <target state="new"><![CDATA[Welcome to the open source %release% release. Using the community friendly <a target="_blank" href="%license_url%">GPL license</a>, + <a target="_blank" href="%gpl_faq_url%">sharing your code</a> is what it's all about.]]></target> + <note>key: dashboard.ez_version.community_severity_non</note> + </trans-unit> + <trans-unit id="bbddea87dd6b56fc0b4d21c28d1cbc2cfd92b754" resname="dashboard.ez_version.end_of_life"> + <source><![CDATA[Welcome to the open source %release% release. Using the community friendly <a target="_blank" href="%license_url%">GPL license</a>, + <a target="_blank" href="%gpl_faq_url%">sharing your code</a> is what it's all about.]]></source> + <target state="new"><![CDATA[Welcome to the open source %release% release. Using the community friendly <a target="_blank" href="%license_url%">GPL license</a>, + <a target="_blank" href="%gpl_faq_url%">sharing your code</a> is what it's all about.]]></target> + <note>key: dashboard.ez_version.end_of_life</note> + </trans-unit> + <trans-unit id="9bad0b8d1efd71dca8071e736f332b3b4218d557" resname="dashboard.ez_version.end_of_life_upgrade"> + <source><![CDATA[Unfortunately %release% has reached <a target="_blank" href="%service_life_url%">end of life</a>, + please plan to upgrade. If you need assistance, don't hesitate to <a target="_blank" href="%contact_url%">contact eZ</a>.]]></source> + <target state="new"><![CDATA[Unfortunately %release% has reached <a target="_blank" href="%service_life_url%">end of life</a>, + please plan to upgrade. If you need assistance, don't hesitate to <a target="_blank" href="%contact_url%">contact eZ</a>.]]></target> + <note>key: dashboard.ez_version.end_of_life_upgrade</note> + </trans-unit> + <trans-unit id="d620cc5d1f36ef74cbf6c042e07c0ad89bf47d9f" resname="dashboard.ez_version.end_of_maintenance"> + <source>Your trial period is coming to an end.</source> + <target state="new">Your trial period is coming to an end.</target> + <note>key: dashboard.ez_version.end_of_maintenance</note> + </trans-unit> + <trans-unit id="7b47468c70176c3f6c78ca0e29b1378e2685d2e1" resname="dashboard.ez_version.end_of_maintenance_contanct"> + <source><![CDATA[<a target="_blank" href="%contact_url%">Contact eZ or its partner(s)</a> to purchase a subscription + and follow the <a target="_blank" href="%install_ee%">online documentation</a> to configure your project.]]></source> + <target state="new"><![CDATA[<a target="_blank" href="%contact_url%">Contact eZ or its partner(s)</a> to purchase a subscription + and follow the <a target="_blank" href="%install_ee%">online documentation</a> to configure your project.]]></target> + <note>key: dashboard.ez_version.end_of_maintenance_contanct</note> + </trans-unit> + <trans-unit id="99cfacc8eb18b57ad83dccc62b053ad0aea20b79" resname="dashboard.ez_version.non_stable"> + <source>Your setup is running with unstable packages, this is not recommended besides when testing updates.</source> + <target state="new">Your setup is running with unstable packages, this is not recommended besides when testing updates.</target> + <note>key: dashboard.ez_version.non_stable</note> + </trans-unit> + <trans-unit id="59967d79ef1f521fd483dbb057f15bc756b80423" resname="dashboard.ez_version.non_stable_ee"> + <source><![CDATA[If you need assistance, don't hesitate to <a target="_blank" href="%support_url%">get in touch with eZ support</a>.]]></source> + <target state="new"><![CDATA[If you need assistance, don't hesitate to <a target="_blank" href="%support_url%">get in touch with eZ support</a>.]]></target> + <note>key: dashboard.ez_version.non_stable_ee</note> + </trans-unit> + <trans-unit id="c1fb5ba9fd547e30a49a4e1895822e69cd4a9dbc" resname="dashboard.ez_version.release_not_determined"> + <source><![CDATA[The system could not find your <code>composer.lock</code> file. It's needed to determine information about + your eZ install, and recommended to be kept on project development to make sure same package versions are used across all environments.]]></source> + <target state="new"><![CDATA[The system could not find your <code>composer.lock</code> file. It's needed to determine information about + your eZ install, and recommended to be kept on project development to make sure same package versions are used across all environments.]]></target> + <note>key: dashboard.ez_version.release_not_determined</note> + </trans-unit> + <trans-unit id="b0b5f4a2312f9a25f310e6d5044046ce0146a9c3" resname="dashboard.ez_version.severity_non"> + <source><![CDATA[Welcome to %name%, check our <a target="_blank" href="%doc_url%">online documentation</a>, <a target="_blank" href="%consulting_url%">consulting</a> + or <a target="_blank" href="%training_url%">training</a> services in order to get the most out of your trial.]]></source> + <target state="new"><![CDATA[Welcome to %name%, check our <a target="_blank" href="%doc_url%">online documentation</a>, <a target="_blank" href="%consulting_url%">consulting</a> + or <a target="_blank" href="%training_url%">training</a> services in order to get the most out of your trial.]]></target> + <note>key: dashboard.ez_version.severity_non</note> + </trans-unit> + <trans-unit id="977ad02930ea5e0ae86eab268d0628786ad6b909" resname="dashboard.ez_version.severity_non_contant"> + <source><![CDATA[<a target="_blank" href="%contact_url%">Contact eZ or its partner(s)</a> to purchase a subscription + and follow the <a target="_blank" href="%install_ee%">online documentation</a> to configure your project.]]></source> + <target state="new"><![CDATA[<a target="_blank" href="%contact_url%">Contact eZ or its partner(s)</a> to purchase a subscription + and follow the <a target="_blank" href="%install_ee%">online documentation</a> to configure your project.]]></target> + <note>key: dashboard.ez_version.severity_non_contant</note> + </trans-unit> + <trans-unit id="10afa9a6e250d82f948ea1e6cbcdf7aac8bd7a4b" resname="dashboard.ez_version.trial_expired"> + <source><![CDATA[Unfortunately your trial period has expired and your <a target="_blank" href="%ttl_url%">TTL license</a> is no longer valid.]]></source> + <target state="new"><![CDATA[Unfortunately your trial period has expired and your <a target="_blank" href="%ttl_url%">TTL license</a> is no longer valid.]]></target> + <note>key: dashboard.ez_version.trial_expired</note> + </trans-unit> + <trans-unit id="fad8721d93ef0b882327327f52921f656a8e206b" resname="dashboard.ez_version.trial_expired_contant"> + <source><![CDATA[<a target="_blank" href="%contact_url%">Contact eZ or its partner(s)</a> to purchase a subscription + and follow the <a target="_blank" href="%install_ee%">online documentation</a> to configure your project.]]></source> + <target state="new"><![CDATA[<a target="_blank" href="%contact_url%">Contact eZ or its partner(s)</a> to purchase a subscription + and follow the <a target="_blank" href="%install_ee%">online documentation</a> to configure your project.]]></target> + <note>key: dashboard.ez_version.trial_expired_contant</note> + </trans-unit> + </body> + </file> +</xliff> diff --git a/Resources/views/themes/admin/dashboard/block/ez.html.twig b/Resources/views/themes/admin/dashboard/block/ez.html.twig new file mode 100644 index 00000000..2386505b --- /dev/null +++ b/Resources/views/themes/admin/dashboard/block/ez.html.twig @@ -0,0 +1,134 @@ +{% trans_default_domain 'dashboard' %} + +{# + Want to edit these messages? + If you are on GPL, as always make sure to share your modifications, as well as your bundles. But to + make sure everyone benefits, please consider contributing modifications to ezsystems/ez-support-tools. +#} + +{% set badge = "" %} +{% set severity = 0 %} +{% set levels = {0: "info", 1: "warning", 2: "danger"} %} +{% set icons = {0: "", 1: "⚠", 2: "⚠"} %} +{% set status %} + {% spaceless %} + {% if not ez.release %} + {% set severity = 1 %} + <div class="alert alert-warning collapse" role="alert" id="systemInfoCollapse"> + {{ 'dashboard.ez_version.release_not_determined'|trans|desc("The system could not find your <code>composer.lock</code> file. It's needed to determine information about + your eZ install, and recommended to be kept on project development to make sure same package versions are used across all environments.")}} + </div> + {% elseif ez.isTrial %} + {% set badge = 'Trial' %} + {% if ez.isEndOfLife %} + {% set severity = 2 %} + <div class="alert alert-danger collapse" role="alert" id="systemInfoCollapse"> + {{ 'dashboard.ez_version.trial_expired'|trans({'%ttl_url%': urls['ttl']})|desc("Unfortunately your trial period has expired and your <a target=\"_blank\" href=\"%ttl_url%\">TTL license</a> is no longer valid.")|raw }} + {{ 'dashboard.ez_version.trial_expired_contant'|trans({'%contact_url%': urls['contact'], '%install_ee%': urls['install_ee']})|desc("<a target=\"_blank\" href=\"%contact_url%\">Contact eZ or its partner(s)</a> to purchase a subscription + and follow the <a target=\"_blank\" href=\"%install_ee%\">online documentation</a> to configure your project.")|raw }} + </div> + {% elseif ez.isEndOfMaintenance %} + {% set severity = 1 %} + <div class="alert alert-warning collapse" role="alert" id="systemInfoCollapse"> + {{ 'dashboard.ez_version.end_of_maintenance'|trans|desc("Your trial period is coming to an end.")}} + {{ 'dashboard.ez_version.end_of_maintenance_contanct'|trans({'%contact_url%': urls['contact'], '%install_ee%': urls['install_ee']}) + |desc("<a target=\"_blank\" href=\"%contact_url%\">Contact eZ or its partner(s)</a> to purchase a subscription + and follow the <a target=\"_blank\" href=\"%install_ee%\">online documentation</a> to configure your project.") + |raw }} + </div> + {% else %} + {% set severity = 0 %} + <div class="alert alert-info collapse" role="alert" id="systemInfoCollapse"> + {{ 'dashboard.ez_version.severity_non'|trans({'%name%': ez.name, '%doc_url%': urls['doc'], '%consulting_url%': urls['consulting_service'], '%training_url%': urls['training_service']}) + |desc('Welcome to %name%, check our <a target=\"_blank\" href=\"%doc_url%\">online documentation</a>, <a target=\"_blank\" href=\"%consulting_url%\">consulting</a> + or <a target=\"_blank\" href=\"%training_url%\">training</a> services in order to get the most out of your trial.') + |raw }} + {{ 'dashboard.ez_version.severity_non_contant'|trans({'%contact_url%': urls['contact'], '%install_ee%': urls['install_ee']}) + |desc("<a target=\"_blank\" href=\"%contact_url%\">Contact eZ or its partner(s)</a> to purchase a subscription + and follow the <a target=\"_blank\" href=\"%install_ee%\">online documentation</a> to configure your project.") + |raw }} + </div> + {% endif %} + {% elseif not ez.isEnterpise %} + {% set badge = 'GPL' %} + {% if ez.isEndOfMaintenance %} + {# In the future with retrival of info from updates.ez.no we can detect missing (public) security fixes and then let this become an error #} + {% set severity = 1 %} + <div class="alert alert-warning collapse" role="alert" id="systemInfoCollapse"> + {{ 'dashboard.ez_version.community_end_of_maintenance'|trans({'%release%': ez.release, '%update_url%': urls['update_url']}) + |desc("Unfortunately %release% open source version has reached end of life, <a target=\"_blank\" href=\"%update_url%\">please upgrade</a>.")}} + <em> + {{ 'dashboard.ez_version.community_end_of_maintenance_upgrade'|trans({ + '%license_url%': urls['license'], + '%ee_product_url%': urls['ee_product'], + '%support_service_url%': urls['support_service'], + '%service_life_url%': urls['service_life'] + }) + |desc("Tip: If you upgrade to eZ Platform Enterprise you'll get access to: + <a target=\"_blank\" href=\"%license_url%\">A business friendly license</a>, + <a target=\"_blank\" href=\"%ee_product_url%\">several productivity features</a>, + <a target=\"_blank\" href=\"%support_service_url%\">professional Support</a> and + <a target=\"_blank\" href=\"%service_life_url%\">longer maintenance periode of your release</a>.") + |raw }} + </em> + </div> + {% else %} + {% set severity = 0 %} + <div class="alert alert-info collapse" role="alert" id="systemInfoCollapse"> + {{ 'dashboard.ez_version.community_severity_non'|trans({ + '%release%': ez.release, + '%license_url%': urls['license'], + '%gpl_faq_url%': urls['gpl_faq'], + }) + |desc("Welcome to the open source %release% release. Using the community friendly <a target=\"_blank\" href=\"%license_url%\">GPL license</a>, + <a target=\"_blank\" href=\"%gpl_faq_url%\">sharing your code</a> is what it's all about.") + |raw }} + </div> + {% endif %} + {% elseif ez.isEndOfLife %} + {# As we don't yet here know if subscription has expired (todo with info from updates.ez.no), this is a warning and not a error #} + {% set severity = 1 %} + {% set badge = 'End of Life' %} + <div class="alert alert-warning collapse" role="alert" id="systemInfoCollapse"> + {{ 'dashboard.ez_version.end_of_life'|trans({ + '%release%': urls['license'], + '%license_url%': urls['ee_product'], + '%gpl_faq_url%': urls['gpl_faq'], + }) + |desc("Welcome to the open source %release% release. Using the community friendly <a target=\"_blank\" href=\"%license_url%\">GPL license</a>, + <a target=\"_blank\" href=\"%gpl_faq_url%\">sharing your code</a> is what it's all about.") + |raw }} + {{ 'dashboard.ez_version.end_of_life_upgrade'|trans({ + '%release%': urls['license'], + '%service_life_url%': urls['service_life'], + '%contact_url%': urls['contact'], + }) + |desc("Unfortunately %release% has reached <a target=\"_blank\" href=\"%service_life_url%\">end of life</a>, + please plan to upgrade. If you need assistance, don't hesitate to <a target=\"_blank\" href=\"%contact_url%\">contact eZ</a>.") + |raw }} + </div> + {% elseif not ez.debug and ez.stability != 'stable' %} + {% set severity = 1 %} + {% set badge = 'Development' %} + <div class="alert alert-warning collapse" role="alert" id="systemInfoCollapse"> + {{ 'dashboard.ez_version.non_stable'|trans|desc("Your setup is running with unstable packages, this is not recommended besides when testing updates.") }} + {% if ez.isEnterpise %} + {{ 'dashboard.ez_version.non_stable_ee'|trans({'%support_url%': urls['support']})|desc("If you need assistance, don't hesitate to <a target=\"_blank\" href=\"%support_url%\">get in touch with eZ support</a>.")|raw }} + {% endif %} + </div> + {% endif %} +{% endspaceless %} +{% endset %} + +<div class="mb-4"> + <br> + <h2 class="mb-3"> + {{ ez.name }} + {{ ez.release }}{% if ez.stability != 'stable' %}{{ ez.release ? '-' : '' }}{{ ez.stability }}{% endif %} + {% if status %} + <a class="badge badge-{{ levels[severity] }}" data-toggle="collapse" href="#systemInfoCollapse">{{ icons[severity]|raw }} {{ badge }}</a> + {% endif %} + </h2> + + {{ status|raw }} +</div> diff --git a/SystemInfo/Collector/EzSystemInfoCollector.php b/SystemInfo/Collector/EzSystemInfoCollector.php new file mode 100644 index 00000000..5ef2eb9b --- /dev/null +++ b/SystemInfo/Collector/EzSystemInfoCollector.php @@ -0,0 +1,219 @@ +<?php + +/** + * File containing the EzcHardwareSystemInfoCollector class. + * + * @copyright Copyright (C) eZ Systems AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace EzSystems\EzSupportToolsBundle\SystemInfo\Collector; + +use EzSystems\EzSupportToolsBundle\SystemInfo\Exception\ComposerLockFileNotFoundException; +use EzSystems\EzSupportToolsBundle\SystemInfo\Value\EzSystemInfo; +use DateTime; + +/** + * Collects information about the eZ installation. + * + * @internal This class will greatly change in the future and should not be used as a api, planned: + * - Get most of this information off updates.ez.no + * - Probably run this as a nightly cronjob to gather summary info + * - Be able to provide warnings to admins when something (config/system setup) is not optimal + * - Be able to give information if important updates are avaiable to install + * - Or be able to tell if install is greatly outdated + * - Be able to give heads up when install is approaching end of life. + */ +class EzSystemInfoCollector implements SystemInfoCollector +{ + /** + * Estimated release dates for given releases. + * + * Mainly for usage for trail to calculate TTL expiry. + */ + const RELEASES = [ + '2.0' => '2017-12-20T23:59:59+00:00', + '2.1' => '2018-03-20T23:59:59+00:00', + '2.2' => '2018-06-20T23:59:59+00:00', + '2.3' => '2018-09-20T23:59:59+00:00', + '2.4' => '2018-12-20T23:59:59+00:00', + '2.5' => '2019-03-20T23:59:59+00:00', // Estimate at time of writing + '3.0' => '2019-06-20T23:59:59+00:00', // Estimate at time of writing + '3.1' => '2019-09-20T23:59:59+00:00', // Estimate at time of writing + ]; + + /** + * Dates for when releases are considered end of maintenance. + * + * Open source releases are considered end of life when this date ias reached. + * + * @Note: Only enterprise/commerce installs recives fixes for security + * issues before the issues are disclosed. Also be aware the link + * below is covering Enterprise/Commerce releases, lenght of + * maintenance for LTS releases may not be as long for open source + * releases as it depends on community maintenance efforts. + * + * @see: https://support.ez.no/Public/Service-Life + */ + const EOM = [ + '2.0' => '2018-03-20T23:59:59+00:00', + '2.1' => '2018-06-20T23:59:59+00:00', + '2.2' => '2018-09-20T23:59:59+00:00', + '2.3' => '2018-12-20T23:59:59+00:00', + '2.4' => '2019-03-20T23:59:59+00:00', + '2.5' => '2022-03-20T23:59:59+00:00', // Estimate at time of writing + '3.0' => '2019-09-20T23:59:59+00:00', // Estimate at time of writing + '3.1' => '2019-12-20T23:59:59+00:00', // Estimate at time of writing + ]; + + /** + * Dates for when Enterprise/Commerce installs are considered end of life. + * + * Meaning when they stop reciving security fixes and support. + * + * @see: https://support.ez.no/Public/Service-Life + */ + const EOL = [ + '2.0' => '2018-06-20T23:59:59+00:00', + '2.1' => '2018-09-20T23:59:59+00:00', + '2.2' => '2019-03-20T23:59:59+00:00', // Extended + '2.3' => '2019-03-20T23:59:59+00:00', + '2.4' => '2019-06-20T23:59:59+00:00', + '2.5' => '2024-03-20T23:59:59+00:00', // Estimate at time of writing + '3.0' => '2019-12-20T23:59:59+00:00', // Estimate at time of writing + '3.1' => '2020-03-20T23:59:59+00:00', // Estimate at time of writing + ]; + + /** + * Vendors we watch for stability (and potentially more). + */ + const PACKAGE_WATCH_REGEX = '/^(doctrine|ezsystems|silversolutions|symfony)\//'; + + /** + * Packages that identifies install as Enterpirse install. + */ + const ENTERPISE_PACKAGES = [ + 'ezsystems/ezplatform-page-builder', + 'ezsystems/flex-workflow', + 'ezsystems/landing-page-fieldtype-bundle', + ]; + + /** + * Packages that identifies install as Commerce install. + */ + const COMMERCE_PACKAGES = [ + 'silversolutions/silver.e-shop', + ]; + + /** + * @var \EzSystems\EzSupportToolsBundle\SystemInfo\Value\ComposerSystemInfo|null + */ + private $composerInfo; + + /** + * @var bool + */ + private $debug; + + /** + * @param \EzSystems\EzSupportToolsBundle\SystemInfo\Collector\JsonComposerLockSystemInfoCollector|\EzSystems\EzSupportToolsBundle\SystemInfo\Collector\SystemInfoCollector $composerCollector + * @param bool $debug + */ + public function __construct(SystemInfoCollector $composerCollector, $debug = false) + { + try { + $this->composerInfo = $composerCollector->collect(); + } catch (ComposerLockFileNotFoundException $e) { + // do nothing + } + $this->debug = $debug; + } + + /** + * Collects information about the eZ distrobution and version. + * + * @return \EzSystems\EzSupportToolsBundle\SystemInfo\Value\EzSystemInfo + */ + public function collect() + { + $ez = new EzSystemInfo(['debug' => $this->debug]); + if ($this->composerInfo === null) { + return $ez; + } + + // The most reliable way to get version is from kernel + // future updates should make sure to detect when kernel version selector is wrong compare to other packages + if (isset($this->composerInfo->packages['ezsystems/ezpublish-kernel'])) { + $ez->release = (string)(((float)$this->composerInfo->packages['ezsystems/ezpublish-kernel']->version) - 5); + } + + if ($package = $this->getFirstPackage(self::ENTERPISE_PACKAGES)) { + $ez->isEnterpise = true; + $ez->isTrial = $package->license === 'TTL-2.0'; + $ez->name = 'eZ Platform Enterprise'; + } + + if ($package = $this->getFirstPackage(self::COMMERCE_PACKAGES)) { + $ez->isCommerce = true; + $ez->isTrial = $ez->isTrial || $package->license === 'TTL-2.0'; + $ez->name = 'eZ Commerce'; + } + + if ($ez->isTrial && isset(self::RELEASES[$ez->release])) { + $months = (new DateTime(self::RELEASES[$ez->release]))->diff(new DateTime())->m; + $ez->isEndOfMaintenance = $months > 3; + $ez->isEndOfLife = $months > 6; + } else { + if (isset(self::EOM[$ez->release])) { + $ez->isEndOfMaintenance = strtotime(self::EOM[$ez->release]) < time(); + } + + if (isset(self::EOL[$ez->release])) { + if (!$ez->isEnterpise) { + $ez->isEndOfLife = $ez->isEndOfMaintenance; + } else { + $ez->isEndOfLife = strtotime(self::EOL[$ez->release]) < time(); + } + } + } + + $ez->stability = $this->getStability(); + + return $ez; + } + + private function getStability() + { + $stabilityFlags = array_flip(JsonComposerLockSystemInfoCollector::STABILITIES); + + // Root package stability + $stabilityFlag = $this->composerInfo->minimumStability !== null ? + $stabilityFlags[$this->composerInfo->minimumStability] : + $stabilityFlags['stable']; + + // Check if any of the watche packages has lower stability then root + foreach ($this->composerInfo->packages as $name => $package) { + if (!preg_match(self::PACKAGE_WATCH_REGEX, $name)) { + continue; + } + + if ($package->stability === 'stable' || $package->stability === null) { + continue; + } + + if ($stabilityFlags[$package->stability] > $stabilityFlag) { + $stabilityFlag = $stabilityFlags[$package->stability]; + } + } + + return JsonComposerLockSystemInfoCollector::STABILITIES[$stabilityFlag]; + } + + private function getFirstPackage($packageNames) + { + foreach ($packageNames as $packageName) { + if (isset($this->composerInfo->packages[$packageName])) { + return $this->composerInfo->packages[$packageName]; + } + } + } +} diff --git a/SystemInfo/Collector/JsonComposerLockSystemInfoCollector.php b/SystemInfo/Collector/JsonComposerLockSystemInfoCollector.php index cea9e8f3..0e1f0ec8 100644 --- a/SystemInfo/Collector/JsonComposerLockSystemInfoCollector.php +++ b/SystemInfo/Collector/JsonComposerLockSystemInfoCollector.php @@ -21,7 +21,7 @@ class JsonComposerLockSystemInfoCollector implements SystemInfoCollector * * Needed as long as we don't want to depend on Composer. */ - private $stabilities = [ + const STABILITIES = [ 0 => 'stable', 5 => 'RC', 10 => 'beta', @@ -34,6 +34,11 @@ class JsonComposerLockSystemInfoCollector implements SystemInfoCollector */ private $lockFile; + /** + * @var Value\ComposerSystemInfo The collected value, cached in case info is collected by other collectors. + */ + private $value; + public function __construct($lockFile) { $this->lockFile = $lockFile; @@ -48,35 +53,72 @@ public function __construct($lockFile) */ public function collect() { + if ($this->value) { + return $this->value; + } + if (!file_exists($this->lockFile)) { throw new Exception\ComposerLockFileNotFoundException($this->lockFile); } $packages = []; + $rootAliases = []; $lockData = json_decode(file_get_contents($this->lockFile), true); + foreach ($lockData['aliases'] as $alias) { + $rootAliases[$alias['package']] = $alias['alias']; + } + + // For PHP 5.6, add variable locally to be able to use isset() on it. + $stabilities = self::STABILITIES; foreach ($lockData['packages'] as $packageData) { - $packages[$packageData['name']] = new Value\ComposerPackage([ + $package = new Value\ComposerPackage([ 'name' => $packageData['name'], - 'version' => $packageData['version'], + 'branch' => $packageData['version'], 'dateTime' => isset($packageData['time']) ? new \DateTime($packageData['time']) : null, 'homepage' => isset($packageData['homepage']) ? $packageData['homepage'] : '', 'reference' => isset($packageData['source']) ? $packageData['source']['reference'] : null, + 'license' => isset($packageData['license'][0]) ? $packageData['license'][0] : null, ]); - if (isset($lockData['stability-flags'][$packageData['name']])) { - $stabilityFlag = (int)$lockData['stability-flags'][$packageData['name']]; + if (isset($lockData['stability-flags'][$package->name])) { + $stabilityFlag = (int)$lockData['stability-flags'][$package->name]; - if (isset($this->stabilities[$stabilityFlag])) { - $packages[$packageData['name']]->stability = $this->stabilities[$stabilityFlag]; + if (isset($stabilities[$stabilityFlag])) { + $package->stability = $stabilities[$stabilityFlag]; } } + + if (isset($rootAliases[$package->name])) { + $package->alias = $rootAliases[$package->name]; + } elseif (isset($packageData['extra']['branch-alias'][$package->branch])) { + $package->alias = $packageData['extra']['branch-alias'][$package->branch]; + } + + self::setNormalizedVersion($package); + + $packages[$packageData['name']] = $package; } ksort($packages, SORT_FLAG_CASE | SORT_STRING); - return new Value\ComposerSystemInfo([ + return $this->value = new Value\ComposerSystemInfo([ 'packages' => $packages, 'minimumStability' => isset($lockData['minimum-stability']) ? $lockData['minimum-stability'] : null, ]); } + + private static function setNormalizedVersion(Value\ComposerPackage $package) + { + $version = $package->alias ? $package->alias : $package->branch; + if ($version[0] === 'v') { + $version = substr($version, 1); + } + + if (strpos($version, 'x-dev')) { + $version = str_replace('-dev', '', $version); + $package->stability = 'dev'; + } + + $package->version = $version; + } } diff --git a/SystemInfo/Value/ComposerPackage.php b/SystemInfo/Value/ComposerPackage.php index 7053acca..2b455c4c 100644 --- a/SystemInfo/Value/ComposerPackage.php +++ b/SystemInfo/Value/ComposerPackage.php @@ -25,14 +25,45 @@ class ComposerPackage extends ValueObject implements SystemInfo public $name; /** - * Version. + * Tag or Branch. * - * Example: v2.7.10 + * Examples: v2.7.10, dev-master + * + * @var string + */ + public $branch; + + /** + * Alias. + * + * Examples: v2.7.x-dev + * + * @var string|null + */ + public $alias = null; + + /** + * Normilized version number. + * + * Uses root-alias or package-aliases if present to try to provide a version number even on branches. + * + * Examples: 2.7.10, 2.8.x * * @var string */ public $version; + /** + * License string. + * + * Only contains the first license on the package, if set. + * + * Examples: 'TTL-2.0', 'GPL-2.0-only' + * + * @var string|null + */ + public $license = null; + /** * Stability. * diff --git a/SystemInfo/Value/EzSystemInfo.php b/SystemInfo/Value/EzSystemInfo.php new file mode 100644 index 00000000..e8ef182a --- /dev/null +++ b/SystemInfo/Value/EzSystemInfo.php @@ -0,0 +1,64 @@ +<?php + +/** + * File containing the EzSystemInfo class. + * + * @copyright Copyright (C) eZ Systems AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace EzSystems\EzSupportToolsBundle\SystemInfo\Value; + +use eZ\Publish\API\Repository\Values\ValueObject; + +/** + * Value for information about the eZ installation. + * + * @internal This class will greatly change in the future and should not be used as an api. + */ +class EzSystemInfo extends ValueObject implements SystemInfo +{ + /** + * @var string + */ + public $name = 'eZ Platform'; + + /** + * @var string|null Either string like '2.5' or null if not detected. + */ + public $release; + + /** + * @var bool + */ + public $isEnterpise = false; + + /** + * @var bool + */ + public $isCommerce = false; + + /** + * @var bool + */ + public $isEndOfMaintenance = true; + + /** + * @var bool + */ + public $isEndOfLife = true; + + /** + * @var bool + */ + public $isTrial = false; + + /** + * @var string One of {@see \EzSystems\EzSupportToolsBundle\SystemInfo\Collector\JsonComposerLockSystemInfoCollector::STABILITIES}. + */ + public $stability; + + /** + * @var bool + */ + public $debug; +} diff --git a/Tests/SystemInfo/Collector/JsonComposerLockSystemInfoCollectorTest.php b/Tests/SystemInfo/Collector/JsonComposerLockSystemInfoCollectorTest.php index 7875d484..9338e88d 100644 --- a/Tests/SystemInfo/Collector/JsonComposerLockSystemInfoCollectorTest.php +++ b/Tests/SystemInfo/Collector/JsonComposerLockSystemInfoCollectorTest.php @@ -24,7 +24,10 @@ public function testCollect() 'packages' => [ 'ezsystems/ezpublish-kernel' => new ComposerPackage([ 'name' => 'ezsystems/ezpublish-kernel', - 'version' => 'dev-master', + 'branch' => 'dev-master', + 'alias' => '6.2.x-dev', + 'version' => '6.2.x', + 'license' => 'GPL-2.0', 'stability' => 'dev', 'dateTime' => new \DateTime('2016-02-28 14:30:53'), 'homepage' => 'http://share.ez.no', @@ -32,21 +35,30 @@ public function testCollect() ]), 'doctrine/dbal' => new ComposerPackage([ 'name' => 'doctrine/dbal', - 'version' => 'v2.5.4', + 'branch' => 'v2.5.4', + 'alias' => null, + 'version' => '2.5.4', + 'license' => 'MIT', 'dateTime' => new \DateTime('2016-01-05 22:11:12'), 'homepage' => 'http://www.doctrine-project.org', 'reference' => 'abbdfd1cff43a7b99d027af3be709bc8fc7d4769', ]), 'symfony/symfony' => new ComposerPackage([ 'name' => 'symfony/symfony', - 'version' => 'v2.7.10', + 'branch' => 'v2.7.10', + 'alias' => null, + 'version' => '2.7.10', + 'license' => 'MIT', 'dateTime' => new \DateTime('2016-02-28 20:37:19'), 'homepage' => 'https://symfony.com', 'reference' => '9a3b6bf6ebee49370aaf15abc1bdeb4b1986a67d', ]), 'zetacomponents/system-information' => new ComposerPackage([ 'name' => 'zetacomponents/system-information', + 'branch' => '1.1', + 'alias' => null, 'version' => '1.1', + 'license' => 'Apache-2.0', 'dateTime' => new \DateTime('2014-09-27 19:26:09'), 'homepage' => 'https://github.com/zetacomponents', 'reference' => 'be0e5b69dde0a51f8d2a036b891964521939769f', diff --git a/composer.json b/composer.json index 7bdff7c7..a42457fc 100644 --- a/composer.json +++ b/composer.json @@ -10,10 +10,12 @@ } ], "require": { - "ezsystems/ezpublish-kernel": "~6.7.8 || ~6.13.4 || ^7.0", + "php": ">=7.1", + "ezsystems/ezpublish-kernel": "^7.4@dev", "ocramius/proxy-manager": "~1.0 || ~2.0", "symfony/proxy-manager-bridge": "^2.8.40 || ^3.4.11", - "zetacomponents/system-information": "^1.1.1" + "zetacomponents/system-information": "^1.1.1", + "ezsystems/ezplatform-admin-ui": "^1.5@dev" }, "require-dev": { "friendsofphp/php-cs-fixer": "~2.14.2", @@ -26,7 +28,7 @@ }, "extra": { "branch-alias": { - "dev-master": "0.2.x-dev" + "dev-master": "1.0.x-dev" } }, "scripts": {