sgd_dashboard-1.0.0-beta1/src/Services/SiteGuardianCronService.php
src/Services/SiteGuardianCronService.php
<?php
namespace Drupal\sgd_dashboard\Services;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\Core\Session\AccountSwitcherInterface;
use Drupal\Core\Session\UserSession;
use Drupal\sgd_dashboard\Entity\Bundle\WebsiteBundle;
use Drupal\sgd_dashboard\Exception\SiteGuardianClientAPIException;
use Drupal\ultimate_cron\Entity\CronJob;
use Drupal\ultimate_cron\CronJobInterface;
use Panlatent\CronExpressionDescriptor\ExpressionDescriptor;
use Psr\Log\LoggerInterface;
/**
* SiteGuardian Cron service.
*
* Provides functions for handling cron jobs.
*/
class SiteGuardianCronService {
use StringTranslationTrait;
/**
* The SGD data service.
*
* @var Drupal\sgd_dashboard\Services\SiteGuardianDataService
*/
protected $dataService;
/**
* The storage handler class for nodes.
*
* @var \Drupal\node\NodeStorage
*/
private $nodeStorage;
/**
* The account switcher service.
*
* @var \Drupal\Core\Session\AccountSwitcherInterface
*/
protected $accountSwitcher;
/**
* The module handler to invoke hooks on.
*
* @var \Drupal\Core\Extension\ModuleHandlerInterface
*/
protected $moduleHandler;
/**
* Logger.
*
* @var Psr\Log\LoggerInterface
*/
protected $logger;
/**
* {@inheritDoc}
*/
public function __construct(SiteGuardianDataService $dataService, EntityTypeManagerInterface $entity, AccountSwitcherInterface $account_switcher, ModuleHandlerInterface $module_handler, LoggerInterface $logger) {
$this->dataService = $dataService;
$this->nodeStorage = $entity->getStorage('node');
$this->accountSwitcher = $account_switcher;
$this->moduleHandler = $module_handler;
$this->logger = $logger;
}
/**
* Adds a Cron job for a website node.
*/
public function addCronJob(WebsiteBundle $node) {
$jobId = $this->getCronJobId($node);
// If no cron job for the node being passed then create one.
if (!CronJob::load($jobId)) {
// We don't want every cron job to run at the exact same time so figure
// out a random time within the morning hours between 2 and 5.
$hour = rand(2, 5);
$minute = rand(0, 59);
$cronRule = "$minute $hour * * *";
$jobTitle = $this->t("Site Guardian: Website refresh for: @title", ['@title' => $node->getTitle()]);
$values = [
'id' => $jobId,
'title' => $jobTitle,
'module' => "sgd_dashboard",
'weight' => 10,
'callback' => 'siteguardian.CronService::refreshCallback',
'scheduler' => [
'id' => 'crontab',
'configuration' => [
'rules' => [$cronRule],
],
],
];
$cronJob = CronJob::create($values);
$cronJob->setThirdPartySetting('sgd_dashboard', 'target_node', $node->id());
$cronJob->save();
}
}
/**
* Updates a Cron job for a website node.
*/
public function updateCronJob(WebsiteBundle $node) {
$jobId = $this->getCronJobId($node);
if ($cronJob = CronJob::load($jobId)) {
$jobTitle = $this->t("Site Guardian: Website refresh for: @title", ['@title' => $node->getTitle()]);
$cronJob->setTitle($jobTitle);
$cronJob->save();
return TRUE;
}
else {
return FALSE;
}
}
/**
* Deletes a Cron job for a website node.
*/
public function deleteCronJob(WebsiteBundle $node) {
$jobId = $this->getCronJobId($node);
if ($cronJob = CronJob::load($jobId)) {
$cronJob->delete();
}
}
/**
* Get the crontab for a website cron job.
*/
public function getCrontab(WebsiteBundle $node) : ?string {
$jobId = $this->getCronJobId($node);
if ($cronJob = CronJob::load($jobId)) {
$crontab = $cronJob->get('scheduler')['configuration']['rules'][0];
return $crontab;
}
return NULL;
}
/**
* Get the crontab for a website cron job as human readable text.
*/
public function getCrontabAsText(WebsiteBundle $node) : ?string {
if ($crontab = $this->getCrontab($node)) {
return (new ExpressionDescriptor($crontab))->getDescription();
}
return $this->t("No cron schedule found.");
}
/**
* Set the crontab for a website cron job.
*/
public function setCrontab(WebsiteBundle $node, string $crontab) : void {
$jobId = $this->getCronJobId($node);
if ($cronJob = CronJob::load($jobId)) {
$scheduler = $cronJob->get('scheduler');
$scheduler['configuration']['rules'][0] = $crontab;
$cronJob->set('scheduler', $scheduler);
$cronJob->save();
}
}
/**
* Cron callback that refreshes the data for a website.
*/
public function refreshCallback(CronJobInterface $job) {
$websiteNodeId = $job->getThirdPartySetting('sgd_dashboard', 'target_node', FALSE);
/** @var \Drupal\sgd_dashboard\Entity\Bundle\WebsiteBundle $websiteNode */
$websiteNode = $this->nodeStorage->load($websiteNodeId);
if ($websiteNode->isPublished()) {
try {
// Need to be able to run as admin (CRON runs as anon) so basic auth
// info can be retreived by the CRON job.
$this->accountSwitcher->switchTo(new UserSession(['uid' => 1]));
$this->dataService->refreshWebsiteFields($websiteNode);
// Fire a hook so any other modules can react to the completion of a
// website refresh.
$this->moduleHandler->invokeAll('site_guardian_dashboard_cron_refresh_complete', [$websiteNode]);
}
catch (SiteGuardianClientAPIException $e) {
$this->logger->error("Error getting website status from %website: @error", [
'%website' => $websiteNode->get('field_url')->value,
'@error' => $e->getMessage(),
]);
}
finally {
// Must allways switch back to the old session.
$this->accountSwitcher->switchBack();
}
}
return TRUE;
}
/**
* Return the cron job id for the website node passed in.
*/
private function getCronJobId(WebsiteBundle $node) : string {
return "sgd_refresh_job_" . $node->id();
}
}
