<?php
namespace Cms\CoreBundle\Service\Twig;
use Cms\AssetsBundle\Model\ElementSettings;
use Cms\AssetsBundle\Model\Structure\MetaStructure;
use Cms\AssetsBundle\Model\Structure\ScriptStructure\LinkedScriptStructure;
use Cms\AssetsBundle\Model\Structure\StyleStructure\LinkedStyleStructure;
use Cms\AssetsBundle\Model\Structure\TitleStructure;
use Cms\ContainerBundle\Entity\Container;
use Cms\ContainerBundle\Entity\Containers\GenericContainer;
use Cms\ContainerBundle\Entity\Containers\IntranetContainer;
use Cms\ContainerBundle\Entity\Containers\PersonalContainer;
use Cms\ContainerBundle\Entity\Containers\StorageContainer;
use Cms\ContainerBundle\Service\ContainerService;
use Cms\CoreBundle\Model\Interfaces\Blameable\BlameableInterface;
use Cms\CoreBundle\Model\Interfaces\Identifiable\IdentifiableInterface;
use Cms\CoreBundle\Model\Interfaces\Lockable\LockableInterface;
use Cms\CoreBundle\Model\Interfaces\Nestable\NestableInterface;
use Cms\CoreBundle\Model\Interfaces\Timestampable\TimestampableInterface;
use Cms\CoreBundle\Service\Aws\S3Wrapper;
use Cms\CoreBundle\Service\Locksmith;
use Cms\CoreBundle\Service\SceneRenderer;
use Cms\CoreBundle\Util\DateTimeUtils;
use Cms\CoreBundle\Util\Doctrine\EntityManager;
use Cms\CoreBundle\Util\Twig\TokenParser\InlineHtmlTokenParser;
use Cms\CoreBundle\Util\Twig\TokenParser\InlineScriptTokenParser;
use Cms\CoreBundle\Util\Twig\TokenParser\InlineStyleTokenParser;
use Cms\DomainBundle\Entity\SslCertificate;
use Cms\DomainBundle\Entity\SslCertificates\CustomSslCertificate;
use Cms\DomainBundle\Entity\SslCertificates\LetsEncryptSslCertificate;
use Cms\FileBundle\Entity\Nodes\File;
use Cms\FileBundle\Entity\Nodes\Files\ImageFile;
use Cms\FileBundle\Entity\Optimizations\ImageOptimization;
use Cms\FileBundle\Service\FileManager;
use Cms\FrontendBundle\Model\FrontendGlobals;
use Cms\ModuleBundle\Entity\Draft;
use Cms\ModuleBundle\Entity\Proxy;
use Cms\ModuleBundle\Model\Interfaces\Shareable\ShareableInterface;
use Cms\ModuleBundle\Model\ModuleConfig;
use Cms\ModuleBundle\Service\DraftManager;
use Cms\SyncBundle\Model\Interfaces\Syncable\SyncableInterface;
use Cms\TenantBundle\Entity\TenantedEntity;
use Cms\ThemeBundle\Entity\Template;
use Cms\ThemeBundle\Service\Twig\Loader\DatabaseLoader;
use Common\Util\Base64;
use Common\Util\Strings;
use Common\Util\Tokens;
use Doctrine\Common\Util\ClassUtils;
use Exception;
use Platform\SecurityBundle\Entity\Identity\Account;
use Symfony\Bridge\Twig\AppVariable;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Contracts\Translation\TranslatorInterface;
use Twig\Environment;
use Twig\Extension\AbstractExtension;
use Twig\Extension\GlobalsInterface;
use Twig\TwigFilter;
use Twig\TwigFunction;
/**
* Class ContextExtension
*
* @package Cms\CoreBundle\Service\Twig
*/
class ContextExtension extends AbstractExtension implements GlobalsInterface
{
/**
* @var TranslatorInterface
*/
private TranslatorInterface $translator;
/**
* @var S3Wrapper
*/
private S3Wrapper $s3Wrapper;
/**
* @var ContainerService
*/
private ContainerService $containerService;
/**
* @var EntityManager
*/
private EntityManager $em;
/**
* @var FileManager
*/
private FileManager $fileManager;
/**
* @var DatabaseLoader
*/
private DatabaseLoader $databaseLoader;
/**
* @var RequestStack
*/
private RequestStack $requestStack;
/**
* @var SceneRenderer
*/
private SceneRenderer $sceneRenderer;
/**
* @var DraftManager
*/
private DraftManager $draftManager;
/**
* @var Locksmith
*/
private Locksmith $locksmith;
/**
* @var GlobalVariables
*/
private GlobalVariables $globalVariables;
/**
* @param TranslatorInterface $translator
* @param S3Wrapper $s3Wrapper
* @param ContainerService $containerService
* @param EntityManager $em
* @param FileManager $fileManager
* @param DatabaseLoader $databaseLoader
* @param RequestStack $requestStack
* @param SceneRenderer $sceneRenderer
* @param DraftManager $draftManager
* @param Locksmith $locksmith
* @param GlobalVariables $globalVariables
*/
public function __construct(
TranslatorInterface $translator,
S3Wrapper $s3Wrapper,
ContainerService $containerService,
EntityManager $em,
FileManager $fileManager,
DatabaseLoader $databaseLoader,
RequestStack $requestStack,
SceneRenderer $sceneRenderer,
DraftManager $draftManager,
Locksmith $locksmith,
GlobalVariables $globalVariables
)
{
$this->translator = $translator;
$this->s3Wrapper = $s3Wrapper;
$this->containerService = $containerService;
$this->em = $em;
$this->fileManager = $fileManager;
$this->databaseLoader = $databaseLoader;
$this->requestStack = $requestStack;
$this->sceneRenderer = $sceneRenderer;
$this->draftManager = $draftManager;
$this->locksmith = $locksmith;
$this->globalVariables = $globalVariables;
}
/**
* {@inheritdoc}
*/
public function getTokenParsers(): array
{
return array(
new InlineScriptTokenParser(),
new InlineStyleTokenParser(),
new InlineHtmlTokenParser(),
new \Cms\CoreBundle\Twig\TokenParser\ScopedIncludeTokenParser(),
new \Cms\CoreBundle\Twig\TokenParser\ScopedEmbedTokenParser(),
);
}
/**
* {@inheritdoc}
*/
public function getFilters(): array
{
return array(
new TwigFilter(
'base64_encode',
array($this, 'base64_encode')
),
new TwigFilter(
'bytes',
array($this, 'bytesFilter')
),
new TwigFilter(
'call',
array($this, 'filterCall')
),
new TwigFilter(
'call_array',
array($this, 'filterCallArray')
),
new TwigFilter(
'regex',
array($this, 'regex')
),
new TwigFilter(
'flatten',
array($this, 'filterFlatten')
),
new TwigFilter(
'arrayify',
array($this, 'filterArrayify')
),
new TwigFilter(
'rot13',
array($this, 'rot13')
),
new TwigFilter(
'scoped',
array($this, 'scoped')
),
new TwigFilter(
'vformat',
'vsprintf'
),
new TwigFilter(
'extract',
array($this, 'extract')
),
);
}
/**
* @param mixed $value
* @param string $property
* @return array
*/
public function extract($value, $property)
{
$results = [];
foreach ($value as $v) {
if (is_array($v)) {
$results[] = $v[$property];
} else if (is_object($v)) {
$refl = new \ReflectionObject($v);
if ($refl->hasProperty($property) && $refl->getProperty($property)->isPublic()) {
$results[] = $refl->getProperty($property)->getValue($v);
} else if (method_exists($v, $property)) {
$results[] = $v->$property();
} else if (method_exists($v, sprintf('get%s', ucfirst($property)))) {
$results[] = $v->{'get'.ucfirst($property)}();
} else {
throw new \Exception();
}
} else {
throw new \Exception();
}
}
return $results;
}
/**
* @param string $value
* @return string
*/
public function base64_encode($value)
{
return Base64::encode((string) $value);
}
/**
* @param mixed $value
* @param string $glue
* @return string
*/
public function filterFlatten($value, $glue = ' ')
{
if (is_array($value)) {
return implode($glue, $value);
}
return trim((string) $value);
}
/**
* @param mixed $value
* @return array
*/
public function filterArrayify($value)
{
if (is_array($value)) {
return $value;
}
return array($value);
}
/**
* @param string $value
* @return string
*/
public function rot13($value)
{
return str_rot13($value);
}
/**
* @param callable $closure
* @return mixed
*/
public function filterCall(callable $closure)
{
// get the remaining arguments, these are passed to the callable
$arguments = array_slice(func_get_args(), 1);
// run and get the result
return call_user_func_array($closure, $arguments);
}
/**
* @param callable $closure
* @param array $args
* @return mixed
*/
public function filterCallArray(callable $closure, array $args)
{
return call_user_func_array($closure, $args);
}
/**
* {@inheritdoc}
*/
public function getFunctions(): array
{
return array(
new TwigFunction(
'blame',
array($this, 'blame'),
array(
'needs_environment' => true,
'is_safe' => array(
'all',
),
)
),
new TwigFunction(
's3_entity',
array($this, 'functionS3Entity'),
[]
),
new TwigFunction(
'instanceof',
array($this, 'functionInstanceof'),
[]
),
new TwigFunction(
'embed_script',
array($this, 'embedScript'),
array(
'is_safe' => array(
'html',
),
)
),
new TwigFunction(
'embed_script_unique',
array($this, 'embedScriptUnique'),
array(
'is_safe' => array(
'html',
),
)
),
new TwigFunction(
'embed_style',
array($this, 'embedStyle'),
array(
'is_safe' => array(
'html',
),
)
),
new TwigFunction(
'embed_meta',
array($this, 'embedMeta'),
array(
'is_safe' => array(
'html',
),
)
),
new TwigFunction(
'embed_title',
array($this, 'embedTitle'),
array(
'is_safe' => array(
'html',
),
)
),
new TwigFunction(
'html_attrs',
array($this, 'htmlAttrs'),
array(
'is_safe' => array(
'html',
),
)
),
new TwigFunction(
'body_attrs',
array($this, 'bodyAttrs'),
array(
'is_safe' => array(
'html',
),
)
),
new TwigFunction(
'curpath',
array($this, 'curpathFunction'),
array(
'needs_environment' => true,
'needs_context' => true,
)
),
new TwigFunction(
'redirpath',
array($this, 'redirpathFunction'),
array(
'needs_environment' => true,
'needs_context' => true,
)
),
new TwigFunction(
'is_type',
array($this, 'functionIsType'),
[]
),
new TwigFunction(
'base64_encode',
array($this, 'functionBase64Encode'),
[]
),
new TwigFunction(
'region',
array($this, 'functionRegion'),
array(
'needs_context' => true,
'is_safe' => array(
'all',
),
)
),
new TwigFunction(
'token',
array($this, 'functionToken'),
[]
),
new TwigFunction(
'headjs_script',
array($this, 'functionHeadJsScript'),
array(
'is_safe' => array(
'all',
),
)
),
new TwigFunction(
'nbsp',
array($this, 'functionNbsp'),
[]
),
new TwigFunction(
'regex',
array($this, 'regex'),
[]
),
new TwigFunction(
'cms_path_prefix',
array($this, 'functionCmsPathPrefix'),
[]
),
new TwigFunction(
'cms_path_host',
array($this, 'functionCmsPathHost'),
[]
),
new TwigFunction(
'ui_container_indent',
array($this, 'ui_container_indent'),
array(
'is_safe' => ['html'],
)
),
new TwigFunction(
'cms_modules_listUrl',
array($this, 'cms_modules_listUrl'),
[]
),
new TwigFunction(
'cms_modules_viewUrl',
array($this, 'cms_modules_viewUrl'),
[]
),
new TwigFunction(
'cms_files_imageopt',
array($this, 'cms_files_imageopt'),
[]
),
new TwigFunction(
'cms_files_drive',
array($this, 'cms_files_drive'),
[]
),
new TwigFunction(
'ui_tree_estimation',
array($this, 'ui_tree_estimation'),
[]
),
new TwigFunction(
'ui_is_locked',
array($this, 'ui_is_locked'),
[]
),
new TwigFunction(
'ui_is_placeholder',
array($this, 'ui_is_placeholder'),
[]
),
new TwigFunction(
'ui_has_draft',
array($this, 'ui_has_draft'),
[]
),
new TwigFunction(
'ui_is_active_draft',
array($this, 'ui_is_active_draft'),
[]
),
new TwigFunction(
'ui_is_synced',
array($this, 'ui_is_synced'),
[]
),
new TwigFunction(
'ui_is_shared',
array($this, 'ui_is_shared'),
[]
),
new TwigFunction(
'ui_is_shareable',
array($this, 'ui_is_shareable'),
[]
),
new TwigFunction(
'cms_abstract',
array($this, 'cms_abstract'),
[]
),
new TwigFunction(
'cms_base_override',
array($this, 'cms_base_override'),
[]
),
new TwigFunction(
'cms_package_override',
array($this, 'cms_package_override'),
array(
'needs_context' => true,
)
),
new TwigFunction(
'cms_theme_override',
array($this, 'cms_theme_override'),
array(
'needs_context' => true,
)
),
new TwigFunction(
'cms_class',
array($this, 'cms_class'),
[]
),
new TwigFunction(
'infscroll',
array($this, 'infscroll'),
[]
),
new TwigFunction(
'ifgzip',
array($this, 'ifgzip'),
array(
'needs_environment' => false,
'needs_context' => true,
)
),
new TwigFunction(
'scoped',
array($this, 'scoped'),
[]
),
new TwigFunction(
'get_class',
array($this, 'get_class'),
[]
),
new TwigFunction(
'ui_ssl_expiration_helpers',
array($this, 'ui_ssl_expiration_helpers'),
[]
),
new TwigFunction(
'is_route',
array($this, 'is_route'),
[]
),
new TwigFunction(
'first_of',
array($this, 'first_of'),
[]
),
new TwigFunction(
'btstamp',
array($this, 'btstamp'),
array(
'needs_environment' => true,
'is_safe' => 'html',
)
),
);
}
/**
* @param Environment $twig
* @param mixed $thing
* @param bool $avatar
* @return string
*/
public function btstamp(Environment $twig, $thing, bool $avatar = true): string
{
$image = null;
$texts = [];
if ($avatar === true && $thing instanceof BlameableInterface) {
if ($thing->getBlamedBy() instanceof Account) {
if ($thing->getBlamedBy()->getSystemProfile()->hasAvatar()) {
$url = $twig->getFunction('s3_entity')->getCallable()($thing->getBlamedBy(), '/avatar/thumb');
} else {
$url = '/ui/images/avatars/avatar-na-36x36.png';
}
$title = $thing->getBlamedBy()->getDisplayName();
} else {
$url = '/ui/images/avatars/avatar-na-36x36.png';
$title = 'SYSTEM';
}
$image = sprintf(
'<img class="user-avatar round" src="%s" alt="%s" title="%s" />',
$url,
$title,
$title
);
}
if ($thing instanceof TimestampableInterface) {
$texts[] = ($thing->isUpdated()) ? 'Updated' : 'Created';
} else if ($thing instanceof BlameableInterface) {
$texts[] = ( ! empty($thing->getUpdatedBy())) ? 'Updated' : 'Created';
} else {
throw new \Exception();
}
if ($thing instanceof BlameableInterface) {
$texts[] = sprintf(
'by %s',
( ! empty($thing->getBlamedBy()))
? $thing->getBlamedBy()->getDisplayName()
: 'SYSTEM'
);
}
if ($thing instanceof TimestampableInterface) {
$texts[] = $twig->getFilter('ui_datetime')->getCallable()($thing->getTimestampedAt());
}
return trim(sprintf(
'%s %s',
$image,
implode(' ', array_map(
function (string $text) use ($twig) {
return $twig->getFilter('escape')->getCallable()($twig, $text);
},
$texts
))
));
}
/**
* @param array $tests
* @param mixed $default
* @return mixed
*/
public function first_of(array $tests, $default = null)
{
foreach ($tests as $result => $test) {
if (is_array($test)) {
if ($test[0] == true) {
return $test[1];
}
}
if ($test == true) {
return $result;
}
}
return $default;
}
/**
* @param string $route
* @param array $params
* @param Request|null $request
* @return bool
*/
public function is_route($route, array $params = [], Request $request = null)
{
// default the request if not given
if (empty($request)) {
$request = $this->requestStack->getMasterRequest();
}
// if route does not match, fail
if ($request->get('_route') !== $route) {
return false;
}
// now see if we have params to check
foreach ($params as $key => $value) {
// must be set in the route params
if ( ! array_key_exists($key, $request->get('_route_params'))) {
return false;
}
// it does exist, so it should match
if ($request->get('_route_params')[$key] !== $value) {
return false;
}
}
// everything matches
return true;
}
public function ui_ssl_expiration_helpers(SslCertificate $certificate)
{
static $lookup = array(
CustomSslCertificate::class => array(
7 => 'danger',
30 => 'warning',
),
LetsEncryptSslCertificate::class => array(
7 => 'danger',
15 => 'warning',
),
);
$diff = intval(DateTimeUtils::current()->diff($certificate->getExpiration())->format('%r%a'));
foreach ($lookup[ClassUtils::getClass($certificate)] as $limit => $helper) {
if ($diff <= $limit) {
return $helper;
}
}
return 'success';
}
public function get_class($object)
{
return ClassUtils::getClass($object);
}
public function scoped(array $variables)
{
return array(
'_args' => $variables,
);
}
public function ifgzip($context, $if, $else)
{
// make sure we have valid arguments
if (empty($if) || empty($else)) {
throw new \Exception();
}
// make sure we have app and can get request
if ( ! array_key_exists('app', $context) || ! $context['app'] instanceof AppVariable) {
// assume gzip not supported
return (string) $else;
}
// code completion
/** @var AppVariable $app */
$app = $context['app'];
// make sure we have a request
if (empty($app->getRequest())) {
// assume gzip not supported
return (string) $else;
}
// get the headers and check if an encoding header is given
$headers = $app->getRequest()->headers;
if ($headers->has('Accept-Encoding')) {
// see if we mention anything about gzip
if (strpos($headers->get('Accept-Encoding'), 'gzip') !== false) {
// should be able to use gzip
return (string) $if;
}
}
// assume gzip not supported
return (string) $else;
}
public function infscroll($url)
{
// reusable regex
$regex = '/(.+\\/)0$/';
// make sure we match
if (preg_match($regex, $url) !== 1) {
throw new \Exception();
}
return preg_replace($regex, '$1{{#}}', $url);
}
/**
* @param object $thing
* @return string|null
*/
public function cms_class($thing)
{
if (is_object($thing)) {
return ClassUtils::getClass($thing);
}
return null;
}
/**
* @param string $path
* @return string
*/
public function cms_base_override($path)
{
return sprintf(
'@package$/%s',
ltrim($path, '/')
);
}
/**
* @param array $context
* @param string $path
* @param Template $theme
* @return string
*/
public function cms_package_override(array $context, $path, Template $theme = null)
{
// if no theme, try to get globals from context; can get theme from that
if (empty($theme) && array_key_exists('_globals', $context) && ! empty($context['_globals'])) {
$globals = $context['_globals'];
if ($globals instanceof FrontendGlobals) {
$theme = $globals->getTheme();
}
}
// return package template
return sprintf(
'@package%s/%s',
($theme !== null) ? $theme->getPackage() : '$',
ltrim($path, '/')
);
}
/**
* @param array $context
* @param string $path
* @param Template $theme
* @return string
*/
public function cms_theme_override(array $context, $path, Template $theme = null)
{
// if no theme, try to get globals from context; can get theme from that
if (empty($theme) && array_key_exists('_globals', $context) && ! empty($context['_globals'])) {
$globals = $context['_globals'];
if ($globals instanceof FrontendGlobals) {
$theme = $globals->getTheme();
}
}
return $this->databaseLoader->determine(
$theme,
$path
);
}
/**
* @param Environment $environment
* @param mixed $thing
* @return string
*/
public function blame(Environment $environment, $thing)
{
$timestamp = '';
if ($thing instanceof TimestampableInterface) {
if ($thing->getTimestampedAt() !== null) {
if ($thing->isUpdated()) {
$timestamp = 'Updated';
} else {
$timestamp = 'Created';
}
$timestamp = sprintf(
'%s %s',
$timestamp,
call_user_func(
$environment->getFilter('ui_relativeDate')->getCallable(),
$thing->getTimestampedAt()
)
);
}
}
$blamestamp = '';
if ($thing instanceof BlameableInterface) {
if ($thing->getBlamedBy() !== null) {
$blamestamp = sprintf(
'by %s',
$thing->getBlamedBy()->getDisplayName()
);
}
}
return trim(sprintf(
'%s %s',
$timestamp,
$blamestamp
));
}
/**
* @param mixed|LockableInterface $thing
* @param Account|null $account
* @return bool
*/
public function ui_is_locked($thing, Account $account = null): bool
{
// SCHOOLNOW: skip if we are passing a sn data type
return $thing instanceof LockableInterface && $this->locksmith->isLocked($thing, $account);
}
/**
* @param mixed|Proxy $thing
* @return bool
*/
public function ui_is_placeholder($thing): bool
{
// SCHOOLNOW: skip if we are passing a sn data type
return $thing instanceof Proxy && $thing->isPlaceholder();
}
/**
* @param mixed|Proxy $thing
* @return bool
*/
public function ui_has_draft($thing): bool
{
// SCHOOLNOW: skip if we are passing a sn data type
return $thing instanceof Proxy && $this->draftManager->hasActiveDraft($thing);
}
/**
* @param mixed|Draft $thing
* @return bool
*/
public function ui_is_active_draft($thing): bool
{
// SCHOOLNOW: skip if we are passing a sn data type
return $thing instanceof Draft && $this->draftManager->isActiveDraft($thing);
}
/**
* @param mixed|SyncableInterface $thing
* @return bool
*/
public function ui_is_synced($thing): bool
{
// SCHOOLNOW: skip if we are passing a sn data type
return $thing instanceof SyncableInterface && $thing->isSynced();
}
/**
* @param mixed|ShareableInterface $thing
* @return bool
*/
public function ui_is_shared($thing): bool
{
// SCHOOLNOW: skip if we are passing a sn data type
return $thing instanceof ShareableInterface && $thing->isShared();
}
/**
* @param mixed|ShareableInterface $thing
* @return bool
*/
public function ui_is_shareable($thing): bool
{
// SCHOOLNOW: skip if we are passing a sn data type
return $thing instanceof ShareableInterface && $thing->isMaster();
}
/**
* @param NestableInterface $node
* @return float
*/
public function ui_tree_estimation(NestableInterface $node)
{
return ((($node->getRight() - $node->getLeft()) - 1) / 2);
}
/**
* @param null $url
* @return string
*/
public function cms_image_fallback($url = null)
{
// if no fallback url given, use our generic one
if ($url === null) {
$url = 'https://via.placeholder.com/1600x1200';
}
// render out the html attribute
return sprintf(
'onerror="this.src = \'%s\';this.onerror = null;"',
$url
);
}
/**
* @param null $optimization
* @return string
* @throws Exception
*/
public function cms_image_placeholder($optimization = null)
{
// if no mask given, assume full
if ($optimization === null) {
$optimization = ImageOptimization::MASKS__GENERIC__FULL;
}
if (is_string($optimization)) {
$constant = ImageOptimization::class.'::'.$optimization;
if ( ! defined($constant)) {
throw new \Exception();
}
$optimization = constant($constant);
}
// get the dimensions based on the mask
$size = ImageOptimization::$sizes[$optimization];
[$width, $height] = array_map(
function($value) {
return intval($value);
},
explode('x', $size)
);
// default sizes if zeroed
if ($width === 0) {
$width = 1600;
}
if ($height === 0) {
$height = 1200;
}
// return a placeholdit url
return sprintf(
'https://via.placeholder.com/%sx%s',
$width,
$height
);
}
/**
* @param $file
* @param null $optimization
* @param null $expires
* @param array $args
* @return string
* @throws Exception
*/
public function cms_files_imageopt($file, $optimization = null, $expires = null, array $args = [])
{
if (is_int($file)) {
$file = $this->em->getRepository(File::class)->findExact($file);
}
if (is_string($file)) {
$file = $this->fileManager->identLoad($file);
}
if ( ! $file instanceof ImageFile) {
//throw new \Exception();
return '/ui/images/placeholders/news/2048.png';
}
if ($optimization === null) {
$optimization = ImageOptimization::MASKS__GENERIC__FULL;
}
if (is_string($optimization)) {
$constant = ImageOptimization::class.'::'.$optimization;
if ( ! defined($constant)) {
throw new \Exception();
}
$optimization = constant($constant);
}
return $this->s3Wrapper->entityUrl(
S3Wrapper::BUCKETS__STORAGE,
$file,
'/optimizations/' . $optimization,
$expires,
$args
);
}
/**
* @param mixed $container
* @param string $path
* @param null $optimization
* @param null $expires
* @param array $args
* @return string|null
*/
public function cms_files_drive($container, $path, $optimization = null, $expires = null, array $args = [])
{
// load up container, should be a drive storage
if ( ! $container instanceof Container) {
$container = $this->em->getRepository(StorageContainer::class)->findOneBy(array(
'slug' => $container,
));
}
if ( ! $container instanceof StorageContainer) {
return null;
}
// value could be percent encoded, decode it
if (strpos($path, '%') !== false) {
$path = rawurldecode($path);
}
// file is a path, need to resolve it
try {
$file = $this->fileManager->resolveFile($container, $path);
} catch (\Exception $e) {
$file = null;
}
if ( ! $file instanceof ImageFile) {
return null;
}
// determine what kind of optimization we want
if ($optimization === null) {
$optimization = ImageOptimization::MASKS__GENERIC__FULL;
}
if (is_string($optimization)) {
$constant = ImageOptimization::class.'::'.$optimization;
if ( ! defined($constant)) {
$optimization = ImageOptimization::MASKS__GENERIC__FULL;
} else {
$optimization = constant($constant);
}
}
// get the s3 url
return $this->s3Wrapper->entityUrl(
S3Wrapper::BUCKETS__STORAGE,
$file,
'/optimizations/' . $optimization,
$expires,
$args
);
}
/**
* @param array $containers
* @param $module
* @param null $extra
* @return string
*/
public function cms_modules_url(array $containers, $module, $extra = null)
{
if ($module instanceof ModuleConfig) {
$module = $module->key();
}
return sprintf(
'%s/%s%s',
$this->functionCmsPathPrefix($containers),
$module,
( ! empty($extra)) ? ('/' . ltrim($extra, '/')) : ''
);
}
/**
* @param array $containers
* @param $module
* @param $item
* @param $extra
* @return string
*/
public function cms_modules_itemUrl(array $containers, $module, $item, $extra)
{
if ($module instanceof ModuleConfig) {
$module = $module->key();
}
if ($item instanceof IdentifiableInterface) {
$item = $item->getId();
}
return sprintf(
'%s/%s/%s/%s',
$this->functionCmsPathPrefix($containers),
$module,
$item,
ltrim($extra, '/')
);
}
/**
* @param Container $container
* @param int $multiplier
* @return string
*/
public function ui_container_indent(Container $container, $icons = false)
{
$icon = null;
if ($icons === true) {
switch (true) {
case empty($container->getParent()) && $container instanceof GenericContainer:
$icon = '';
break;
case empty($container->getParent()) && $container instanceof IntranetContainer:
$icon = '';
break;
case empty($container->getParent()) && $container instanceof PersonalContainer:
$icon = '';
break;
case empty($container->getParent()) && $container instanceof StorageContainer:
$icon = '';
break;
default:
$icon = '';
}
}
return str_repeat(Strings::nbsp(), ($container->getLevel() * 4)) . (( ! empty($icon)) ? ($icon . ' ') : ' ') . htmlspecialchars($container->getName());
}
/**
* @param array|Container[] $containers
* @param string $scheme
* @return string
*/
public function functionCmsPathPrefix($containers, $scheme = null)
{
if ( ! is_array($containers)) {
$containers = array($containers);
}
return $this->containerService->getFrontLink(
$containers[0],
$scheme
);
}
/**
* @param Container|Container[] $containers
* @return string
* @throws \Exception
*/
public function functionCmsPathHost($containers)
{
if ( ! is_array($containers)) {
$containers = array($containers);
}
/** @var array|Container[] $containers */
$container = $containers[count($containers) - 1];
if ($container->getDomain() === null) {
throw new \Exception();
}
return 'http://'.$container->getDomain()->getHost();
}
/**
* @param int $count
* @return string
*/
public function functionNbsp($count = 1)
{
// NOTE: this is the utf8 non-breaking space character
return str_repeat('Â ', $count);
}
/**
* @param string $var
* @param string $find
* @param string $replace
* @return string
*/
public function regex($var, $find = '', $replace = '')
{
if (is_string($var) && strlen($var)) {
$var = preg_replace($find, $replace, $var);
}
return $var;
}
/**
* @param string $url
* @param string $callback
* @return string
* @throws Exception
*/
public function functionHeadJsScript($url, $callback = '')
{
return sprintf(
'
;(function(window, document, $, undefined) {
if (window.head !== undefined) {
window.head.load(\'%s\', function() {
%s
});
}
})(window, document, jQuery);
',
$url,
$callback
);
}
/**
* @param TenantedEntity $entity
* @param string $subKey
* @param mixed $expires
* @param array $args
* @return string
* @throws Exception
*/
public function functionS3Entity(TenantedEntity $entity, $subKey, $expires = null, array $args = [])
{
$key = $this->s3Wrapper->entityKey($entity, $subKey);
$url = $this->s3Wrapper->url(S3Wrapper::BUCKETS__STORAGE, $key, $expires, $args);
return $url;
}
/**
* @param mixed $thing
* @param string $class
* @return bool
*/
public function functionInstanceof($thing, $class)
{
if ( ! is_object($thing)) {
return false;
}
return (ClassUtils::getClass($thing) === $class || is_subclass_of($thing, $class));
}
/**
* @param int|null $length
* @return string
*/
public function functionToken($length = null)
{
return Tokens::generate($length);
}
/**
* @param array $context
* @param string $key
* @return string
* @throws Exception
*/
public function functionRegion(array $context, $key)
{
if (array_key_exists('_regions', $context) === false) {
throw new Exception();
}
return $context['_regions'][$key];
}
/**
* @param string $var
* @return string
*/
public function functionBase64Encode($var)
{
return Base64::encode($var);
}
/**
* @param mixed $var
* @param string $type
* @return bool
* @throws Exception
*/
public function functionIsType($var, $type)
{
switch ($type) {
case 'array':
return is_array($var);
break;
case 'boolean':
case 'bool':
return is_bool($var);
break;
case 'float':
return is_float($var);
break;
case 'integer':
case 'int':
return is_int($var);
break;
case 'numeric':
return is_numeric($var);
break;
case 'object':
return is_object($var);
break;
case 'scalar':
return is_scalar($var);
break;
case 'string':
return is_string($var);
break;
case 'null':
return is_null($var);
break;
default:
throw new Exception();
}
}
/**
* @param string $url
* @param string|null $type
* @param int|null $rank
* @param string|null $group
* @return string
*/
public function embedScriptUnique($url, $type = null, $rank = null, $group = null)
{
// check title
if ($url === null) {
throw new Exception();
}
// obtain the document tool from current
$documentSettings = $this->sceneRenderer->currentScene()->getAssetsOrganizer();
// create the title
$structure = new LinkedScriptStructure($url, $type);
// check if it exists
if ( ! $documentSettings->getScripts()->check($structure, $group)) {
// register
$documentSettings->getScripts()->add($structure, $rank, $group);
}
/*
return sprintf(
'<!-- SCRIPT | LINKED | %s | %s | %s | %s -->',
($group !== null) ? $group : ScriptStructureBag::DEFAULTS__GROUP,
($rank !== null) ? $rank : ScriptStructureBag::DEFAULTS__PRIORITY,
($type !== null) ? $type : ScriptStructure::DEFAULTS__TYPE,
$url
);
*/
return '';
}
/**
* @param string $url
* @param string|null $type
* @param int|null $rank
* @param string|null $group
* @return string
*/
public function embedScript($url, $type = null, $rank = null, $group = null)
{
// check title
if ($url === null) {
throw new Exception();
}
// obtain the document tool from current
$documentSettings = $this->sceneRenderer->currentScene()->getAssetsOrganizer();
// create the title
$structure = new LinkedScriptStructure($url, $type);
// register
$documentSettings->getScripts()->add($structure, $rank, $group);
/*
return sprintf(
'<!-- SCRIPT | LINKED | %s | %s | %s | %s -->',
($group !== null) ? $group : ScriptStructureBag::DEFAULTS__GROUP,
($rank !== null) ? $rank : ScriptStructureBag::DEFAULTS__PRIORITY,
($type !== null) ? $type : ScriptStructure::DEFAULTS__TYPE,
$url
);
*/
return '';
}
/**
* @param string $url
* @param string|null $type
* @param int|null $rank
* @param string|null $group
* @return string
* @throws Exception
*/
public function embedStyle($url, $type = null, $rank = null, $group = null)
{
// check title
if ($url === null) {
throw new Exception();
}
// obtain the document tool from current
$documentSettings = $this->sceneRenderer->currentScene()->getAssetsOrganizer();
// create the title
$structure = new LinkedStyleStructure($url, $type);
// register
$documentSettings->getStyles()->add($structure, $rank, $group);
/*
return sprintf(
'<!-- STYLE | LINKED | %s | %s | %s | %s -->',
($group !== null) ? $group : StyleStructureBag::DEFAULTS__GROUP,
($rank !== null) ? $rank : StyleStructureBag::DEFAULTS__PRIORITY,
($type !== null) ? $type : 'text/css',
$url
);
*/
return '';
}
/**
* @param string $name
* @param string|null $value
* @param int|null $rank
* @param string|null $group
* @return string
* @throws Exception
*/
public function embedMeta($name, $value = null, $rank = null, $group = null)
{
// check title
if ($name === null) {
throw new Exception();
}
// obtain the document tool from current
$documentSettings = $this->sceneRenderer->currentScene()->getAssetsOrganizer();
// create the title
$structure = new MetaStructure($name, $value, $rank);
// register
$documentSettings->getMetas()->add($structure, $group);
/*
return sprintf(
'<!-- META | %s | %s | %s | %s -->',
($group !== null) ? $group : StyleStructureBag::DEFAULTS__GROUP,
($rank !== null) ? $rank : StyleStructureBag::DEFAULTS__PRIORITY,
$name,
($value !== null) ? $value : ''
);
*/
return '';
}
/**
* @param string $title
* @param int $rank
* @return string
* @throws Exception
*/
public function embedTitle($title, $rank = null)
{
// check title
if ($title === null) {
throw new Exception();
}
$title = $this->translator->trans($title);
// obtain the document tool from current
$documentSettings = $this->sceneRenderer->currentScene()->getAssetsOrganizer();
// create the title
$structure = new TitleStructure($title, $rank);
// register
$documentSettings->getTitles()->add($structure);
/*
return sprintf(
'<!-- TITLE | %s | %s -->',
($rank !== null) ? $rank : StyleStructureBag::DEFAULTS__PRIORITY,
$title
);
*/
return '';
}
/**
* @param string $action
* @param mixed $arg1
* @param mixed $arg2
* @param mixed $arg3
* @return string
* @throws Exception
*/
public function htmlAttrs($action, $arg1 = null, $arg2 = null, $arg3 = null)
{
// obtain the document tool from current
$documentSettings = $this->sceneRenderer->currentScene()->getAssetsOrganizer();
// call helper
return $this->attrs($documentSettings->getHtmlTag(), $action, $arg1, $arg2, $arg3);
}
/**
* @param string $action
* @param mixed $arg1
* @param mixed $arg2
* @param mixed $arg3
* @return string
* @throws Exception
*/
public function bodyAttrs($action, $arg1 = null, $arg2 = null, $arg3 = null)
{
// obtain the document tool from current
$documentSettings = $this->sceneRenderer->currentScene()->getAssetsOrganizer();
// call helper
return $this->attrs($documentSettings->getBodyTag(), $action, $arg1, $arg2, $arg3);
}
/**
* @param ElementSettings $settings
* @param string $action
* @param mixed $arg1
* @param mixed $arg2
* @param mixed $arg3
* @return string
* @throws Exception
*/
private function attrs(ElementSettings $settings, $action, $arg1 = null, $arg2 = null, $arg3 = null)
{
// action is required
if ($action === null) {
throw new Exception();
}
// action must be a function on the class
if (method_exists($settings, $action) === false) {
throw new Exception();
}
// just do the call
$settings->$action($arg1, $arg2, $arg3);
/*
return sprintf(
'<!-- ATTR | %s | %s | %s | %s | %s -->',
get_class($settings),
$action,
($arg1 !== null) ? $arg1 : '',
($arg2 !== null) ? $arg2 : '',
($arg3 !== null) ? $arg3 : ''
);
*/
return '';
}
/**
* @param Environment $environment
* @param array $context
* @param null $route
* @param array $merges
* @param array $removals
* @return mixed
*/
public function curpathFunction(
Environment $environment,
array $context,
$route = null,
array $merges = [],
array $removals = []
) {
// get env globals
/** @var AppVariable $globals */
$globals = $context['app'];
// get current url params
$params = $globals->getRequest()->get('_route_params');
// merge in what we need
$params = array_merge($params, $merges);
// remove what we don't
foreach ($removals as $removal) {
unset($params[$removal]);
}
// handle nulls
foreach (array_keys($params) as $key) {
if ($params[$key] === null) {
unset($params[$key]);
}
}
if (empty($params['return'])) {
// enforce route
if ($route === null) {
$route = $globals->getRequest()->get('_route');
}
// call the path function
$call = $environment->getFunction('path')->getCallable();
$result = $call($route, $params);
} else {
$result = $params['return'];
}
// return the result
return $result;
}
/**
* @param Environment $environment
* @param array $context
* @param null $route
* @param array $merges
* @param array $removals
* @return mixed
*/
public function redirpathFunction(
Environment $environment,
array $context,
$route = null,
array $merges = [],
array $removals = []
) {
// get env globals
/** @var AppVariable $globals */
$globals = $context['app'];
// see if we have a redirectTo field
$request = $globals->getRequest();
if ($request->query->has('redirectTo')) {
return $request->query->get('redirectTo');
}
// get current url params
$params = $globals->getRequest()->get('_route_params');
// merge in what we need
$params = array_merge($params, $merges);
// remove what we don't
foreach ($removals as $removal) {
unset($params[$removal]);
}
// handle nulls
foreach (array_keys($params) as $key) {
if ($params[$key] === null) {
unset($params[$key]);
}
}
if (empty($params['return'])) {
// enforce route
if ($route === null) {
$route = $globals->getRequest()->get('_route');
}
// call the path function
$call = $environment->getFunction('path')->getCallable();
$result = $call($route, $params);
} else {
$result = $params['return'];
}
// return the result
return $result;
}
/**
* @param int $value
* @param string $unit Supports null, 'B', 'KB', 'MB', 'GB', 'TB', 'PB',
* 'EB', 'ZB', 'YB'. Default null.
* @param int $precision
* @param string $separate
* @return string
* @throws Exception
*/
public function bytesFilter($value, $unit = 'B', $precision = 2, $separate = ' ')
{
static $suffixes = array('B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB');
if (empty($value)) {
$value = 0;
}
if ($value === 0) {
$base = array_search($unit, $suffixes);
if ($base === false) {
$base = 0;
}
return '0' . $separate . $suffixes[$base];
}
$value = abs($value);
$place = intval(floor(log($value, 1024)));
$num = round($value / pow(1024, $place), $precision);
return (min(1, max(-1, $value)) * $num) . $separate . $suffixes[$place];
}
/**
* @param string|null $abstract
* @param string $content
* @param int $length
* @return string
*/
public function cms_abstract($abstract, $content, $length = 90)
{
if (empty($abstract)) {
$abstract = '';
}
$abstract = trim($abstract);
return trim(html_entity_decode(
( ! empty($abstract))
? $abstract
: mb_strimwidth(strip_tags($content), 0, $length, '...')
,
ENT_QUOTES
));
}
/**
* {@inheritdoc}
*/
public function getGlobals(): array
{
return array(
'cms' => $this->globalVariables,
);
}
}