391 lines
9.9 KiB
PHP
Executable File
391 lines
9.9 KiB
PHP
Executable File
<?php
|
|
|
|
namespace Mautic\DashboardBundle\Event;
|
|
|
|
use Mautic\CacheBundle\Cache\CacheProviderTagAwareInterface;
|
|
use Mautic\CoreBundle\Event\CommonEvent;
|
|
use Mautic\CoreBundle\Helper\CacheStorageHelper;
|
|
use Mautic\CoreBundle\Security\Permissions\CorePermissions;
|
|
use Mautic\DashboardBundle\Entity\Widget;
|
|
use Mautic\DashboardBundle\Exception\CouldNotFormatDateTimeException;
|
|
use Symfony\Contracts\Translation\TranslatorInterface;
|
|
|
|
class WidgetDetailEvent extends CommonEvent
|
|
{
|
|
public const DASHBOARD_CACHE_TAG = 'dashboard_widget';
|
|
|
|
protected $type;
|
|
|
|
protected $template;
|
|
|
|
protected $templateData = [];
|
|
|
|
protected $errorMessage;
|
|
|
|
protected $uniqueId;
|
|
|
|
protected $cacheDir;
|
|
|
|
protected $uniqueCacheDir;
|
|
|
|
protected $cacheTimeout;
|
|
|
|
protected float $startTime;
|
|
|
|
protected $loadTime = 0;
|
|
|
|
private string $cacheKeyPath = 'dashboard.widget.';
|
|
|
|
private bool $isPreview = false;
|
|
|
|
public function __construct(private TranslatorInterface $translator, private CorePermissions $security, protected Widget $widget, private ?CacheProviderTagAwareInterface $cacheProvider = null)
|
|
{
|
|
$this->startTime = microtime(true);
|
|
$this->setWidget($widget);
|
|
}
|
|
|
|
/**
|
|
* Act as widget preview without data.
|
|
*/
|
|
public function setPreview(bool $isPreview): void
|
|
{
|
|
$this->isPreview = $isPreview;
|
|
}
|
|
|
|
/**
|
|
* Is preview without data?
|
|
*/
|
|
public function isPreview(): bool
|
|
{
|
|
return $this->isPreview;
|
|
}
|
|
|
|
/**
|
|
* Return unique key, uses legacy methods for BC.
|
|
*/
|
|
public function getCacheKey(): string
|
|
{
|
|
$cacheKey = [
|
|
$this->getUniqueWidgetId(),
|
|
];
|
|
|
|
$params = $this->getWidget()->getParams();
|
|
|
|
foreach (['dateTo', 'dateFrom'] as $dateParameter) {
|
|
if (isset($params[$dateParameter])) {
|
|
try {
|
|
$date = $this->castDateTimeToString($params[$dateParameter]);
|
|
$cacheKey[] = $date;
|
|
} catch (CouldNotFormatDateTimeException) {
|
|
}
|
|
}
|
|
}
|
|
|
|
// If there are no additional parameters we return uniqueWidgetId as a cache key
|
|
// Otherwise we return hashed $cacheKey value
|
|
$cacheKey = (1 == count($cacheKey)) ? $this->getUniqueWidgetId() : substr(md5(implode('', $cacheKey)), 0, 16);
|
|
|
|
return $this->cacheKeyPath.$cacheKey;
|
|
}
|
|
|
|
/**
|
|
* Set the cache dir.
|
|
*
|
|
* @param string $cacheDir
|
|
* @param mixed|null $uniqueCacheDir
|
|
*/
|
|
public function setCacheDir($cacheDir, $uniqueCacheDir = null): void
|
|
{
|
|
$this->cacheDir = $cacheDir;
|
|
$this->uniqueCacheDir = $uniqueCacheDir;
|
|
}
|
|
|
|
/**
|
|
* Set the cache timeout.
|
|
*
|
|
* @param string $cacheTimeout
|
|
*/
|
|
public function setCacheTimeout($cacheTimeout): void
|
|
{
|
|
$this->cacheTimeout = (int) $cacheTimeout;
|
|
}
|
|
|
|
/**
|
|
* Set the widget type.
|
|
*
|
|
* @param string $type
|
|
*/
|
|
public function setType($type): void
|
|
{
|
|
$this->type = $type;
|
|
}
|
|
|
|
/**
|
|
* Get the widget type.
|
|
*
|
|
* @return string $type
|
|
*/
|
|
public function getType()
|
|
{
|
|
return $this->type;
|
|
}
|
|
|
|
/**
|
|
* Set the widget entity.
|
|
*/
|
|
public function setWidget(Widget $widget): void
|
|
{
|
|
$this->widget = $widget;
|
|
|
|
$params = $widget->getParams();
|
|
|
|
// Set required params if undefined
|
|
if (!isset($params['timeUnit'])) {
|
|
$params['timeUnit'] = null;
|
|
}
|
|
|
|
if (!isset($params['amount'])) {
|
|
$params['amount'] = null;
|
|
}
|
|
|
|
if (!isset($params['dateFormat'])) {
|
|
$params['dateFormat'] = null;
|
|
}
|
|
|
|
if (!isset($params['filter'])) {
|
|
$params['filter'] = [];
|
|
}
|
|
|
|
$widget->setParams($params);
|
|
|
|
$this->setType($widget->getType());
|
|
$this->setCacheTimeout($widget->getCacheTimeout());
|
|
}
|
|
|
|
/**
|
|
* Returns the widget entity.
|
|
*
|
|
* @return Widget $widget
|
|
*/
|
|
public function getWidget()
|
|
{
|
|
return $this->widget;
|
|
}
|
|
|
|
/**
|
|
* Set the widget template.
|
|
*
|
|
* @param string $template
|
|
*/
|
|
public function setTemplate($template): void
|
|
{
|
|
$this->template = $template;
|
|
$this->widget->setTemplate($template);
|
|
}
|
|
|
|
/**
|
|
* Get the widget template.
|
|
*
|
|
* @return string $template
|
|
*/
|
|
public function getTemplate()
|
|
{
|
|
return $this->template;
|
|
}
|
|
|
|
/**
|
|
* Set the widget template data.
|
|
*
|
|
* @param bool|null $skipCache
|
|
*
|
|
* @throws \Psr\Cache\InvalidArgumentException
|
|
*/
|
|
public function setTemplateData(array $templateData, $skipCache = false): void
|
|
{
|
|
$this->templateData = $templateData;
|
|
$this->widget->setTemplateData($templateData);
|
|
$this->widget->setLoadTime(abs(microtime(true) - $this->startTime));
|
|
|
|
if ($this->usesLegacyCache()) {
|
|
// Store the template data to the cache
|
|
if (!$skipCache && $this->cacheDir && $this->widget->getCacheTimeout() > 0) {
|
|
$cache = new CacheStorageHelper(CacheStorageHelper::ADAPTOR_FILESYSTEM, $this->uniqueCacheDir, null, $this->cacheDir);
|
|
// must pass a DateTime object or a int of seconds to expire as 3rd attribute to set().
|
|
$expireTime = $this->widget->getCacheTimeout() * 60;
|
|
|
|
$cache->set($this->getUniqueWidgetId(), $templateData, (int) $expireTime);
|
|
}
|
|
}
|
|
|
|
$cItem = $this->cacheProvider->getItem($this->getCacheKey());
|
|
if ($this->widget->getCacheTimeout()) {
|
|
$cItem->expiresAfter((int) $this->widget->getCacheTimeout() * 60); // This is in minutes
|
|
}
|
|
$cItem->set($templateData);
|
|
$cItem->tag(self::DASHBOARD_CACHE_TAG);
|
|
|
|
$this->cacheProvider->save($cItem);
|
|
}
|
|
|
|
/**
|
|
* Get the widget template data.
|
|
*
|
|
* @return array<mixed> $templateData
|
|
*/
|
|
public function getTemplateData()
|
|
{
|
|
return $this->templateData;
|
|
}
|
|
|
|
/**
|
|
* Set en error message.
|
|
*
|
|
* @param string $errorMessage
|
|
*/
|
|
public function setErrorMessage($errorMessage): void
|
|
{
|
|
$this->errorMessage = $errorMessage;
|
|
$this->widget->setErrorMessage($errorMessage);
|
|
}
|
|
|
|
/**
|
|
* Get an error message.
|
|
*
|
|
* @return string $errorMessage
|
|
*/
|
|
public function getErrorMessage()
|
|
{
|
|
return $this->errorMessage;
|
|
}
|
|
|
|
/**
|
|
* Build a unique ID from type and widget params.
|
|
*
|
|
* @return string
|
|
*/
|
|
public function getUniqueWidgetId()
|
|
{
|
|
if ($this->uniqueId) {
|
|
return $this->uniqueId;
|
|
}
|
|
|
|
$params = $this->getWidget()->getParams();
|
|
// Unset dateFrom and dateTo since they constantly change
|
|
unset($params['dateFrom'], $params['dateTo']);
|
|
|
|
$uniqueSettings = [
|
|
'params' => $params,
|
|
'width' => $this->getWidget()->getWidth(),
|
|
'height' => $this->getWidget()->getHeight(),
|
|
'locale' => $this->translator->getLocale(),
|
|
];
|
|
|
|
return $this->uniqueId = $this->getType().'_'.substr(md5(json_encode($uniqueSettings)), 0, 16);
|
|
}
|
|
|
|
/**
|
|
* @throws \Psr\Cache\InvalidArgumentException
|
|
* Checks the cache for the widget data.
|
|
* If cache exists, it sets the TemplateData.
|
|
*/
|
|
public function isCached(): bool
|
|
{
|
|
if (!$this->cacheDir && $this->usesLegacyCache()) {
|
|
return false;
|
|
}
|
|
|
|
if ($this->usesLegacyCache()) {
|
|
$cache = new CacheStorageHelper(CacheStorageHelper::ADAPTOR_FILESYSTEM, $this->uniqueCacheDir, null, $this->cacheDir);
|
|
$data = $cache->get($this->getUniqueWidgetId(), $this->cacheTimeout);
|
|
|
|
if ($data) {
|
|
$this->widget->setCached(true);
|
|
$this->setTemplateData($data, true);
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
$cachedItem = $this->cacheProvider->getItem($this->getCacheKey());
|
|
if (!$cachedItem->isHit()) {
|
|
return false;
|
|
}
|
|
|
|
$this->widget->setCached(true);
|
|
$this->setTemplateData($cachedItem->get());
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Get the Translator object.
|
|
*
|
|
* @return TranslatorInterface
|
|
*/
|
|
public function getTranslator()
|
|
{
|
|
return $this->translator;
|
|
}
|
|
|
|
/**
|
|
* Check if the user has at least one permission of defined array of permissions.
|
|
*/
|
|
public function hasPermissions(array $permissions): bool
|
|
{
|
|
if (!$this->security) {
|
|
return true;
|
|
}
|
|
$perm = $this->security->isGranted($permissions, 'RETURN_ARRAY');
|
|
|
|
return in_array(true, $perm);
|
|
}
|
|
|
|
/**
|
|
* Check if the user has defined permission to see the widgets.
|
|
*
|
|
* @param string $permission
|
|
*
|
|
* @return bool
|
|
*/
|
|
public function hasPermission($permission)
|
|
{
|
|
if (!$this->security) {
|
|
return true;
|
|
}
|
|
|
|
return $this->security->isGranted($permission);
|
|
}
|
|
|
|
/**
|
|
* Checks for cache type. This event should be created by factory thus not legacy approach.
|
|
*/
|
|
private function usesLegacyCache(): bool
|
|
{
|
|
return is_null($this->cacheProvider);
|
|
}
|
|
|
|
/**
|
|
* We need to cast DateTime objects to strings to use them in the cache key.
|
|
*
|
|
* @param mixed|null $value
|
|
*
|
|
* @throws CouldNotFormatDateTimeException
|
|
*/
|
|
private function castDateTimeToString($value): string
|
|
{
|
|
if ($value instanceof \DateTimeInterface) {
|
|
// We use RFC 2822 format because it includes timezone
|
|
return $value->format('r');
|
|
}
|
|
|
|
try {
|
|
$value = strval($value);
|
|
} catch (\Exception) {
|
|
throw new CouldNotFormatDateTimeException();
|
|
}
|
|
|
|
return $value;
|
|
}
|
|
}
|