sgd_dashboard-1.0.0-beta1/src/Entity/SgdWebsiteData.php
src/Entity/SgdWebsiteData.php
<?php
declare(strict_types=1);
namespace Drupal\sgd_dashboard\Entity;
use Drupal\Core\Entity\ContentEntityBase;
use Drupal\Core\Entity\EntityChangedTrait;
use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Datetime\DrupalDateTime;
use Drupal\Core\Field\BaseFieldDefinition;
use Drupal\sgd_dashboard\SgdWebsiteDataInterface;
use Drupal\user\EntityOwnerTrait;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\sgd_dashboard\Exception\EndOfLifeClientException;
/**
* Defines the sgd website data entity class.
*
* @ContentEntityType(
* id = "sgd_website_data",
* label = @Translation("Site Guardian Dashboard Website data entity"),
* label_collection = @Translation("Site Guardian Dashboard Website data entities"),
* label_singular = @Translation("Site Guardian Dashboard website data entity"),
* label_plural = @Translation("Site Guardian Dashboard website data entities"),
* label_count = @PluralTranslation(
* singular = "@count Site Guardian Dashboard website data entity",
* plural = "@count Site Guardian Dashboard website data entities",
* ),
* handlers = {
* "list_builder" = "Drupal\sgd_dashboard\SgdWebsiteDataListBuilder",
* "views_data" = "Drupal\sgd_dashboard\SgdWebsiteDataEntityViewsData",
* "form" = {
* "add" = "Drupal\sgd_dashboard\Form\SgdWebsiteDataForm",
* "edit" = "Drupal\sgd_dashboard\Form\SgdWebsiteDataForm",
* "delete" = "Drupal\Core\Entity\ContentEntityDeleteForm",
* "delete-multiple-confirm" = "Drupal\Core\Entity\Form\DeleteMultipleForm",
* },
* "route_provider" = {
* "html" = "Drupal\sgd_dashboard\Routing\SgdWebsiteDataHtmlRouteProvider",
* },
* },
* base_table = "sgd_website_data",
* admin_permission = "administer sgd_website_data",
* entity_keys = {
* "id" = "id",
* "label" = "label",
* "uuid" = "uuid",
* "owner" = "uid",
* },
* links = {
* "collection" = "/admin/content/sgd-website-data",
* "add-form" = "/sgd-website-data/add",
* "canonical" = "/sgd-website-data/{sgd_website_data}",
* "edit-form" = "/sgd-website-data/{sgd_website_data}",
* "delete-form" = "/sgd-website-data/{sgd_website_data}/delete",
* "delete-multiple-form" = "/admin/content/sgd-website-data/delete-multiple",
* },
* )
*/
class SgdWebsiteData extends ContentEntityBase implements SgdWebsiteDataInterface {
use EntityChangedTrait;
use EntityOwnerTrait;
use StringTranslationTrait;
/**
* {@inheritdoc}
*/
public function preSave(EntityStorageInterface $storage): void {
parent::preSave($storage);
if (!$this->getOwnerId()) {
// If no owner has been set explicitly, make the anonymous user the owner.
$this->setOwnerId(0);
}
}
/**
* {@inheritdoc}
*/
public static function baseFieldDefinitions(EntityTypeInterface $entity_type): array {
$fields = parent::baseFieldDefinitions($entity_type);
$fields['label'] = BaseFieldDefinition::create('string')
->setLabel(t('Label'))
->setRequired(TRUE)
->setSetting('max_length', 255)
->setDisplayOptions('form', [
'type' => 'string_textfield',
'weight' => -5,
])
->setDisplayConfigurable('form', TRUE)
->setDisplayOptions('view', [
'label' => 'hidden',
'type' => 'string',
'weight' => -5,
])
->setDisplayConfigurable('view', TRUE);
$fields['contrib_secure'] = BaseFieldDefinition::create('boolean')
->setLabel(t('Contrib security'))
->setDescription(t('A boolean indicating whether all the contributed module versions in use are secure or not.'))
->setDefaultValue(FALSE)
->setSettings(['on_label' => 'Secure', 'off_label' => 'Not secure'])
->setDisplayOptions('view', [
'label' => 'visible',
'type' => 'boolean',
'weight' => -4,
])
->setDisplayOptions('form', [
'type' => 'boolean_checkbox',
'weight' => -4,
])
->setDisplayConfigurable('view', TRUE);
$fields['core_secure'] = BaseFieldDefinition::create('boolean')
->setLabel(t('Core security'))
->setDescription(t('A boolean indicating whether Drupal core is secure or not.'))
->setDefaultValue(FALSE)
->setSettings(['on_label' => 'Secure', 'off_label' => 'Not secure'])
->setDisplayOptions('view', [
'label' => 'visible',
'type' => 'boolean',
'weight' => -3,
])
->setDisplayOptions('form', [
'type' => 'boolean_checkbox',
'weight' => -3,
])
->setDisplayConfigurable('view', TRUE);
$fields['drupal_version'] = BaseFieldDefinition::create('string')
->setLabel(t('Drupal version'))
->setDescription(t('The string representation of the Drupal version.'))
->setSettings([
'max_length' => 255,
'text_processing' => 0,
])
->setDefaultValue('')
->setDisplayOptions('view', [
'label' => 'above',
'type' => 'string',
'weight' => -2,
])
->setDisplayOptions('form', [
'type' => 'string_textfield',
'weight' => -2,
])
->setDisplayConfigurable('view', TRUE);
$fields['php_version'] = BaseFieldDefinition::create('string')
->setLabel(t('PHP version'))
->setDescription(t('The string representation of the PHP version.'))
->setSettings([
'max_length' => 255,
'text_processing' => 0,
])
->setDefaultValue('')
->setDisplayOptions('view', [
'label' => 'above',
'type' => 'string',
'weight' => -1,
])
->setDisplayOptions('form', [
'type' => 'string_textfield',
'weight' => -1,
])
->setDisplayConfigurable('view', TRUE);
$fields['db_version'] = BaseFieldDefinition::create('string')
->setLabel(t('Database version'))
->setDescription(t('The string representation of the database version.'))
->setSettings([
'max_length' => 255,
'text_processing' => 0,
])
->setDefaultValue('')
->setDisplayOptions('view', [
'label' => 'above',
'type' => 'string',
'weight' => 0,
])
->setDisplayOptions('form', [
'type' => 'string_textfield',
'weight' => 0,
])
->setDisplayConfigurable('view', TRUE);
$fields['data_core'] = BaseFieldDefinition::create('string_long')
->setLabel(t('Core data'))
->setDescription(t('SGD Core data from site_guardian_api module.'))
->setRequired(TRUE)
->setDisplayOptions('view', [
'label' => 'visible',
'type' => 'basic_string',
'weight' => 1,
])
->setDisplayOptions('form', [
'type' => 'string_textarea',
'weight' => 1,
'settings' => ['rows' => 4],
])
->setDisplayConfigurable('view', TRUE);
$fields['data_server_benchmarks'] = BaseFieldDefinition::create('string_long')
->setLabel(t('Server Benchmark data'))
->setDescription(t('Server Benchmark data from sgd_server_benchmarks module.'))
->setRequired(FALSE)
->setDisplayOptions('view', [
'label' => 'visible',
'type' => 'basic_string',
'weight' => 2,
])
->setDisplayOptions('form', [
'type' => 'string_textarea',
'weight' => 2,
'settings' => ['rows' => 4],
])
->setDisplayConfigurable('view', TRUE);
$fields['data_php_status'] = BaseFieldDefinition::create('string_long')
->setLabel(t('PHP data'))
->setDescription(t('PHP data from sgd_php_status module.'))
->setRequired(FALSE)
->setDisplayOptions('view', [
'label' => 'visible',
'type' => 'basic_string',
'weight' => 3,
])
->setDisplayOptions('form', [
'type' => 'string_textarea',
'weight' => 3,
'settings' => ['rows' => 4],
])
->setDisplayConfigurable('view', TRUE);
$fields['data_user_status'] = BaseFieldDefinition::create('string_long')
->setLabel(t('User data'))
->setDescription(t('User data from sgd_user_status module.'))
->setRequired(FALSE)
->setDisplayOptions('view', [
'label' => 'visible',
'type' => 'basic_string',
'weight' => 4,
])
->setDisplayOptions('form', [
'type' => 'string_textarea',
'weight' => 4,
'settings' => ['rows' => 4],
])
->setDisplayConfigurable('view', TRUE);
$fields['data_watchdog_summary'] = BaseFieldDefinition::create('string_long')
->setLabel(t('Watchdog summary data'))
->setDescription(t('Watchdog summary data from sgd_watchdog_summary module.'))
->setRequired(FALSE)
->setDisplayOptions('view', [
'label' => 'visible',
'type' => 'basic_string',
'weight' => 5,
])
->setDisplayOptions('form', [
'type' => 'string_textarea',
'weight' => 5,
'settings' => ['rows' => 4],
])
->setDisplayConfigurable('view', TRUE);
$fields['data_php_memory_usage'] = BaseFieldDefinition::create('string_long')
->setLabel(t('PHP Memory Usage data'))
->setDescription(t('PHP Memory Usage data from memory_profiler_plus module.'))
->setRequired(FALSE)
->setDisplayOptions('view', [
'label' => 'visible',
'type' => 'basic_string',
'weight' => 5,
])
->setDisplayOptions('form', [
'type' => 'string_textarea',
'weight' => 5,
'settings' => ['rows' => 4],
])
->setDisplayConfigurable('view', TRUE);
$fields['status'] = BaseFieldDefinition::create('boolean')
->setLabel(t('Status'))
->setDefaultValue(TRUE)
->setSetting('on_label', 'Enabled')
->setDisplayOptions('form', [
'type' => 'boolean_checkbox',
'settings' => [
'display_label' => FALSE,
],
'weight' => 0,
])
->setDisplayConfigurable('form', TRUE)
->setDisplayOptions('view', [
'type' => 'boolean',
'label' => 'above',
'weight' => 0,
'settings' => [
'format' => 'enabled-disabled',
],
])
->setDisplayConfigurable('view', TRUE);
$fields['uid'] = BaseFieldDefinition::create('entity_reference')
->setLabel(t('Author'))
->setSetting('target_type', 'user')
->setDefaultValueCallback(self::class . '::getDefaultEntityOwner')
->setDisplayOptions('form', [
'type' => 'entity_reference_autocomplete',
'settings' => [
'match_operator' => 'CONTAINS',
'size' => 60,
'placeholder' => '',
],
'weight' => 15,
])
->setDisplayConfigurable('form', TRUE)
->setDisplayOptions('view', [
'label' => 'above',
'type' => 'author',
'weight' => 15,
])
->setDisplayConfigurable('view', TRUE);
$fields['created'] = BaseFieldDefinition::create('created')
->setLabel(t('Authored on'))
->setDescription(t('The time that the SGD Website data entity was created.'))
->setDisplayOptions('view', [
'label' => 'above',
'type' => 'timestamp',
'weight' => 20,
])
->setDisplayConfigurable('form', TRUE)
->setDisplayOptions('form', [
'type' => 'datetime_timestamp',
'weight' => 20,
])
->setDisplayConfigurable('view', TRUE);
$fields['changed'] = BaseFieldDefinition::create('changed')
->setLabel(t('Changed'))
->setDescription(t('The time that the SGD Website data entity was last edited.'));
return $fields;
}
/**
* Returns the parent website node Id of the website data entity.
*/
public function getParentNodeId() {
// Get the node id of the parent website node.
$query = \Drupal::entityQuery('node')
->condition('type', 'website')
->condition('field_sgd_website_data', $this->id())
->accessCheck(FALSE);
$results = $query->execute();
return reset($results);
}
/**
* Get the support status of the website's Drupal version.
*
* @return Drupal\Core\StringTranslation\TranslatableMarkup
* Drupal Version Status info as a string - 'ok', 'warning' or 'danger'.
*/
public function getDrupalSupportStatus(): TranslatableMarkup {
$product = 'drupal';
$version = $this->get('drupal_version')->value;
if (!empty($version) && (int) $version >= 8) {
$cycle = implode('.', array_slice(explode('.', $version, 3), 0, 2, TRUE));
}
else {
return $this->t('danger');
}
try {
$eolClient = \Drupal::service('siteguardian.EndOfLifeClientService');
$statusData = $eolClient->getSingleCycle($product, $cycle);
}
catch (EndOfLifeClientException $e) {
\Drupal::logger('site_guardian')->error("Error getting endoflife.date status for %product: @error", [
'%product' => ucfirst($product),
'@error' => $e->getMessage(),
]);
return $this->t('danger');
}
$now = time();
if (!empty($statusData)) {
$support = strtotime($statusData['support']);
// If EOL is false then there is no EOL specified otherwise get the timestamp.
$eol = $statusData['eol'] == FALSE ? FALSE : strtotime($statusData['eol']);
if ($eol == FALSE || ($support >= $now && $eol >= $now)) {
$status = $this->t('ok');
}
elseif ($support < $now && $eol >= $now) {
$status = $this->t('warning');
}
else {
$status = $this->t('danger');
}
}
else {
$status = $this->t('not-available');
}
return $status;
}
/**
* Get a string indicating the website's Drupal version's support status.
*
* @return Drupal\Core\StringTranslation\TranslatableMarkup
* A more user-friendly message about Drupal version support status.
*/
public function getDrupalSupportStatusMessage(): TranslatableMarkup {
$product = 'drupal';
$version = $this->get('drupal_version')->value;
if (!empty($version)) {
$cycle = implode('.', array_slice(explode('.', $version, 3), 0, 2, TRUE));
}
else {
return $this->t("Unable to determine Drupal version or support status.");
}
try {
$eolClient = \Drupal::service('siteguardian.EndOfLifeClientService');
$statusData = $eolClient->getSingleCycle($product, $cycle);
}
catch (EndOfLifeClientException $e) {
\Drupal::logger('site_guardian')->error("Error getting endoflife.date status for %product: @error", [
'%product' => ucfirst($product),
'@error' => $e->getMessage(),
]);
return $this->t("EndOfLife API was unavailable.");
}
if (!empty($statusData)) {
$supported = (new DrupalDateTime($statusData['support'], 'UTC'))->format('l jS \of F Y');
$eol = $statusData['eol'] ? (new DrupalDateTime($statusData['eol'], 'UTC'))->format('l jS \of F Y') : $this->t('unspecified date');
$statusMessage = $this->t("Active support until :supported, Security support until :eol.", [
':supported' => $supported,
':eol' => $eol,
]);
}
else {
$statusMessage = $this->t('Detailed status support info is not available for this version - please visit https://endoflife.date/drupal instead.');
}
return $statusMessage;
}
/**
* Get the support status of PHP for the website.
*
* @return Drupal\Core\StringTranslation\TranslatableMarkup
* PHP Status info as a string 'ok', 'warning', 'danger'
*/
public function getPhpSupportStatus(): TranslatableMarkup {
$product = 'php';
$version = $this->get('php_version')->value;
if (!empty($version)) {
$cycle = implode('.', array_slice(explode('.', $version, 3), 0, 2, TRUE));
}
else {
return $this->t('danger');
}
// Get the PHP version support status.
try {
$eolClient = \Drupal::service('siteguardian.EndOfLifeClientService');
$statusData = $eolClient->getSingleCycle($product, $cycle);
}
// If the API had an exception then we need to handle it.
catch (EndOfLifeClientException $e) {
\Drupal::logger('site_guardian')->error("Error getting PHP endoflife status for %product: @error", [
'%product' => $product,
'@error' => $e->getMessage(),
]);
// We couldn't find out the status so we'll go nuclear and say everything
// is bad.
return $this->t('danger');
}
// Work out what to return - ok, warning or none.
$now = time();
$support = strtotime($statusData['support']);
$eol = strtotime($statusData['eol']);
// If has both active and security support the 'ok'.
if ($support >= $now && $eol >= $now) {
$status = $this->t('ok');
}
// If no active support but still have security support then 'warning'.
elseif ($support < $now && $eol >= $now) {
$status = $this->t('warning');
}
// Anything else is 'danger'.
else {
$status = $this->t('danger');
}
return $status;
}
/**
* Return a string message indicating the support status of the PHP version.
*
* @return Drupal\Core\StringTranslation\TranslatableMarkup
* A more user-friendly message about PHP version support status.
*/
public function getPhpSupportStatusMessage(): TranslatableMarkup {
$product = 'php';
$version = $this->get('php_version')->value;
if (!empty($version)) {
$cycle = implode('.', array_slice(explode('.', $version, 3), 0, 2, TRUE));
}
else {
return $this->t("Unable to determine PHP version or support status.");
}
// Get the PHP version support status.
try {
$eolClient = \Drupal::service('siteguardian.EndOfLifeClientService');
$statusData = $eolClient->getSingleCycle($product, $cycle);
}
// If the API had an exception then we need to handle it.
catch (EndOfLifeClientException $e) {
\Drupal::logger('site_guardian')->error("Error getting PHP endoflife status for %product: @error", [
'%product' => $product,
'@error' => $e->getMessage(),
]);
// We couldn't find out the status so we'll go nuclear and say everything
// is bad.
return $this->t('EndofLife API was unavailable.');
}
// Now we have the product cycle info we can build a descriptive string.
$supported = (new DrupalDateTime($statusData['support'], 'UTC'))->format('l jS \of F Y');
$eol = (new DrupalDateTime($statusData['eol'], 'UTC'))->format('l jS \of F Y');
return $this->t("Active support until :supported, Security support until :eol.", [
':supported' => $supported,
':eol' => $eol,
]);
}
/**
* Get the support status of the Database for the website.
*
* @return Drupal\Core\StringTranslation\TranslatableMarkup
* DB Status info as a string 'ok', 'warning', 'danger'
*/
public function getDbSupportStatus(): TranslatableMarkup {
// Now try to figure out what we are using so we can ask API for
// appropriate info.
$dbVersionString = $this->get('db_version')->value;
if (!empty($dbVersionString)) {
['product' => $product, 'cycle' => $cycle] = $this->getDbProductAndCycle($dbVersionString);
}
else {
return $this->t('danger');
}
// Get the DB version support status.
try {
$eolClient = \Drupal::service('siteguardian.EndOfLifeClientService');
$statusData = $eolClient->getSingleCycle($product, $cycle);
}
// If the API had an exception then we need to handle it.
catch (EndOfLifeClientException $e) {
\Drupal::logger('site_guardian')->error("Error getting DB endoflife status for %product: @error", [
'%product' => $product,
'@error' => $e->getMessage(),
]);
// We couldn't find out the status so we'll go nuclear and say everything
// is bad.
return $this->t('danger');
}
// Work out what to return - ok, warning or none. If no eol status then
// return danger.
if (!empty($statusData['eol'])) {
$now = time();
switch ($product) {
case 'mariadb':
$eol = strtotime($statusData['eol']);
if ($eol < $now) {
$status = $this->t('danger');
}
elseif ($eol < $now + (60 * 60 * 24 * 30 * 3)) {
$status = $this->t('warning');
}
else {
$status = $this->t('ok');
}
break;
case 'mysql':
$eol = strtotime($statusData['eol']);
if ($eol < $now) {
$status = $this->t('danger');
}
elseif ($eol < $now + (60 * 60 * 24 * 30 * 3)) {
$status = $this->t('warning');
}
else {
$status = $this->t('ok');
}
break;
}
return $status;
}
else {
return $this->t('danger');
}
}
/**
* Return a string message inidcating the support status of the DB version.
*
* @return Drupal\Core\StringTranslation\TranslatableMarkup
* A more user-friendly message about DB version support status.
*/
public function getDbSupportStatusMessage(): TranslatableMarkup {
// Now try to figure out what we are using so we can ask API for
// appropriate info.
$dbVersionString = $this->get('db_version')->value;
if (!empty($dbVersionString)) {
['product' => $product, 'cycle' => $cycle] = $this->getDbProductAndCycle($dbVersionString);
}
else {
return $this->t("Unable to determine DB version or support status.");
}
// Get the DB version support status.
try {
$eolClient = \Drupal::service('siteguardian.EndOfLifeClientService');
$statusData = $eolClient->getSingleCycle($product, $cycle);
}
// If the API had an exception then we need to handle it.
catch (EndOfLifeClientException $e) {
\Drupal::logger('site_guardian')->error("Error getting DB endoflife status for %product: @error", [
'%product' => $product,
'@error' => $e->getMessage(),
]);
// We couldn't find out the status so we'll go nuclear and say everything
// is bad.
return $this->t('EndofLife API was unavailable for :product.', [':product' => $product]);
}
// Now we have the product cycle info we can build a descriptive string.
if (!empty($statusData['eol'])) {
$eol = (new DrupalDateTime($statusData['eol'], 'UTC'))->format('l jS \of F Y');
// If eol date is in future.
if (strtotime($statusData['eol']) > time()) {
return $this->t("Support ends on :eol.", [':eol' => $eol]);
}
else {
return $this->t("Support ended on :eol.", [':eol' => $eol]);
}
}
else {
return $this->t("No end of life information available.");
}
}
/**
* Return the support cycle of the DB version.
*
* This is parsed from the version returned from the DB and is
* not an exact science as there are no standards.
*/
private function getDbProductAndCycle($versionString): array {
// If the version contains 'MariaDB' then we know its Maria DB.
if (stripos($versionString, 'mariadb') !== FALSE) {
$product = 'mariadb';
preg_match('/^(\d+\.\d+\.\d+)/', $versionString, $matches);
$cycle = implode('.', array_slice(explode('.', $matches[1], 3), 0, 2, TRUE));
}
// If the version contains 'PostgreSQL' then we know its Maria DB.
elseif (stripos($versionString, 'PostgreSQL') !== FALSE) {
$product = 'postgresql';
preg_match('/\b(\d+\.\d+)\b/', $versionString, $matches);
$cycle = $matches[1];
}
// If the version contains 'Microsoft' then we know its Microsoft.
elseif (stripos($versionString, 'Microsoft') !== FALSE) {
$product = 'mssqlserver';
preg_match('/\b(\d+\.\d+\.\d+\.\d+)\b/', $versionString, $matches);
$cycle = $matches[1];
}
// Otherwise assume Mysql.
else {
$product = 'mysql';
preg_match('/^(\d+\.\d+\.\d+)/', $versionString, $matches);
$cycle = implode('.', array_slice(explode('.', $matches[1], 3), 0, 2, TRUE));
}
return ['product' => $product, 'cycle' => $cycle];
}
}
