For more information about this repository, visit the project page at https://www.drupal.org/project/twig_tweak
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
356 lines
11 KiB
356 lines
11 KiB
<?php |
|
|
|
namespace Drupal\twig_tweak; |
|
|
|
use Drupal\Core\Config\ConfigFactoryInterface; |
|
use Drupal\Core\Entity\EntityTypeManagerInterface; |
|
use Drupal\Core\Menu\MenuActiveTrailInterface; |
|
use Drupal\Core\Menu\MenuLinkTreeInterface; |
|
use Drupal\Core\Routing\RouteMatchInterface; |
|
use Drupal\Core\Site\Settings; |
|
use Drupal\Core\Utility\Token; |
|
use Drupal\image\Entity\ImageStyle; |
|
|
|
/** |
|
* Twig extension with some useful functions and filters. |
|
*/ |
|
class TwigExtension extends \Twig_Extension { |
|
|
|
/** |
|
* The entity type manager. |
|
* |
|
* @var \Drupal\Core\Entity\EntityTypeManagerInterface |
|
*/ |
|
protected $entityTypeManager; |
|
|
|
/** |
|
* The token service. |
|
* |
|
* @var \Drupal\Core\Utility\Token |
|
*/ |
|
protected $token; |
|
|
|
/** |
|
* The configuration factory. |
|
* |
|
* @var \Drupal\Core\Config\ConfigFactoryInterface |
|
*/ |
|
protected $configFactory; |
|
|
|
/** |
|
* The route match. |
|
* |
|
* @var \Drupal\Core\Routing\RouteMatchInterface |
|
*/ |
|
protected $routeMatch; |
|
|
|
/** |
|
* The menu link tree service. |
|
* |
|
* @var \Drupal\Core\Menu\MenuLinkTreeInterface |
|
*/ |
|
protected $menuTree; |
|
|
|
/** |
|
* The active menu trail service. |
|
* |
|
* @var \Drupal\Core\Menu\MenuActiveTrailInterface |
|
*/ |
|
protected $menuActiveTrail; |
|
|
|
/** |
|
* TwigExtension constructor. |
|
* |
|
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager |
|
* The entity type manager. |
|
* @param \Drupal\Core\Utility\Token $token |
|
* The token service. |
|
* @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory |
|
* The configuration factory. |
|
* @param \Drupal\Core\Routing\RouteMatchInterface $route_match |
|
* The route match. |
|
* @param \Drupal\Core\Menu\MenuLinkTreeInterface $menu_tree |
|
* The menu tree service. |
|
* @param \Drupal\Core\Menu\MenuActiveTrailInterface $menu_active_trail |
|
* The active menu trail service. |
|
*/ |
|
public function __construct(EntityTypeManagerInterface $entity_type_manager, Token $token, ConfigFactoryInterface $config_factory, RouteMatchInterface $route_match, MenuLinkTreeInterface $menu_tree, MenuActiveTrailInterface $menu_active_trail) { |
|
$this->entityTypeManager = $entity_type_manager; |
|
$this->token = $token; |
|
$this->configFactory = $config_factory; |
|
$this->routeMatch = $route_match; |
|
$this->menuTree = $menu_tree; |
|
$this->menuActiveTrail = $menu_active_trail; |
|
} |
|
|
|
/** |
|
* {@inheritdoc} |
|
*/ |
|
public function getFunctions() { |
|
return [ |
|
new \Twig_SimpleFunction('drupal_view', 'views_embed_view'), |
|
new \Twig_SimpleFunction('drupal_block', [$this, 'drupalBlock']), |
|
new \Twig_SimpleFunction('drupal_token', [$this, 'drupalToken']), |
|
new \Twig_SimpleFunction('drupal_entity', [$this, 'drupalEntity']), |
|
new \Twig_SimpleFunction('drupal_field', [$this, 'drupalField']), |
|
new \Twig_SimpleFunction('drupal_menu', [$this, 'drupalMenu']), |
|
new \Twig_SimpleFunction('drupal_config', [$this, 'drupalConfig']), |
|
// Wrap drupal_set_message() because it returns some value which is not |
|
// suitable for Twig template. |
|
new \Twig_SimpleFunction('drupal_set_message', [$this, 'drupalSetMessage']), |
|
]; |
|
} |
|
|
|
/** |
|
* {@inheritdoc} |
|
*/ |
|
public function getFilters() { |
|
$filters = [ |
|
new \Twig_SimpleFilter('token_replace', [$this, 'tokenReplaceFilter']), |
|
new \Twig_SimpleFilter('preg_replace', [$this, 'pregReplaceFilter']), |
|
new \Twig_SimpleFilter('image_style', [$this, 'imageStyle']), |
|
]; |
|
// PHP filter should be enabled in settings.php file. |
|
if (Settings::get('twig_tweak_enable_php_filter')) { |
|
$filters[] = new \Twig_SimpleFilter('php', [$this, 'phpFilter']); |
|
} |
|
return $filters; |
|
} |
|
|
|
/** |
|
* {@inheritdoc} |
|
*/ |
|
public function getName() { |
|
return 'twig_tweak'; |
|
} |
|
|
|
/** |
|
* Builds the render array for the provided block. |
|
* |
|
* @param mixed $id |
|
* The ID of the block to render. |
|
* |
|
* @return null|array |
|
* A render array for the block or NULL if the block does not exist. |
|
*/ |
|
public function drupalBlock($id) { |
|
$block = $this->entityTypeManager->getStorage('block')->load($id); |
|
return $block ? |
|
$this->entityTypeManager->getViewBuilder('block')->view($block) : ''; |
|
} |
|
|
|
/** |
|
* Replaces a given tokens with appropriate value. |
|
* |
|
* @param string $token |
|
* A replaceable token. |
|
* @param array $data |
|
* (optional) An array of keyed objects. For simple replacement scenarios |
|
* 'node', 'user', and others are common keys, with an accompanying node or |
|
* user object being the value. Some token types, like 'site', do not |
|
* require any explicit information from $data and can be replaced even if |
|
* it is empty. |
|
* @param array $options |
|
* (optional) A keyed array of settings and flags to control the token |
|
* replacement process. |
|
* |
|
* @return string |
|
* The token value. |
|
* |
|
* @see \Drupal\Core\Utility\Token::replace() |
|
*/ |
|
public function drupalToken($token, array $data = [], array $options = []) { |
|
return $this->token->replace("[$token]", $data, $options); |
|
} |
|
|
|
/** |
|
* Returns the render array for an entity. |
|
* |
|
* @param string $entity_type |
|
* The entity type. |
|
* @param mixed $id |
|
* The ID of the entity to render. |
|
* @param string $view_mode |
|
* (optional) The view mode that should be used to render the entity. |
|
* @param string $langcode |
|
* (optional) For which language the entity should be rendered, defaults to |
|
* the current content language. |
|
* |
|
* @return null|array |
|
* A render array for the entity or NULL if the entity does not exist. |
|
*/ |
|
public function drupalEntity($entity_type, $id = NULL, $view_mode = NULL, $langcode = NULL) { |
|
$entity = $id ? |
|
$this->entityTypeManager->getStorage($entity_type)->load($id) : |
|
$this->routeMatch->getParameter($entity_type); |
|
if ($entity) { |
|
$render_controller = $this->entityTypeManager->getViewBuilder($entity_type); |
|
return $render_controller->view($entity, $view_mode, $langcode); |
|
} |
|
return NULL; |
|
} |
|
|
|
/** |
|
* Returns the render array for a single entity field. |
|
* |
|
* @param string $field_name |
|
* The field name. |
|
* @param string $entity_type |
|
* The entity type. |
|
* @param mixed $id |
|
* The ID of the entity to render. |
|
* @param string $view_mode |
|
* (optional) The view mode that should be used to render the field. |
|
* @param string $langcode |
|
* (optional) Language code to load translation. |
|
* |
|
* @return null|array |
|
* A render array for the field or NULL if the value does not exist. |
|
*/ |
|
public function drupalField($field_name, $entity_type, $id = NULL, $view_mode = 'default', $langcode = NULL) { |
|
$entity = $id ? |
|
$this->entityTypeManager->getStorage($entity_type)->load($id) : |
|
$this->routeMatch->getParameter($entity_type); |
|
if ($langcode && $entity->hasTranslation($langcode)) { |
|
$entity = $entity->getTranslation($langcode); |
|
} |
|
if (isset($entity->{$field_name})) { |
|
return $entity->{$field_name}->view($view_mode); |
|
} |
|
return NULL; |
|
} |
|
|
|
/** |
|
* Returns the render array for Drupal menu. |
|
* |
|
* @param string $menu_name |
|
* The name of the menu. |
|
* @param int $level |
|
* (optional) Initial menu level. |
|
* @param int $depth |
|
* (optional) Maximum number of menu levels to display. |
|
* |
|
* @return array |
|
* A render array for the menu. |
|
*/ |
|
public function drupalMenu($menu_name, $level = 1, $depth = 0) { |
|
$parameters = $this->menuTree->getCurrentRouteMenuTreeParameters($menu_name); |
|
|
|
// Adjust the menu tree parameters based on the block's configuration. |
|
$parameters->setMinDepth($level); |
|
// When the depth is configured to zero, there is no depth limit. When depth |
|
// is non-zero, it indicates the number of levels that must be displayed. |
|
// Hence this is a relative depth that we must convert to an actual |
|
// (absolute) depth, that may never exceed the maximum depth. |
|
if ($depth > 0) { |
|
$parameters->setMaxDepth(min($level + $depth - 1, $this->menuTree->maxDepth())); |
|
} |
|
|
|
$tree = $this->menuTree->load($menu_name, $parameters); |
|
$manipulators = [ |
|
['callable' => 'menu.default_tree_manipulators:checkAccess'], |
|
['callable' => 'menu.default_tree_manipulators:generateIndexAndSort'], |
|
]; |
|
$tree = $this->menuTree->transform($tree, $manipulators); |
|
return $this->menuTree->build($tree); |
|
} |
|
|
|
/** |
|
* Gets data from this configuration. |
|
* |
|
* @param string $name |
|
* The name of the configuration object to construct. |
|
* @param string $key |
|
* A string that maps to a key within the configuration data. |
|
* |
|
* @return mixed |
|
* The data that was requested. |
|
*/ |
|
public function drupalConfig($name, $key) { |
|
return $this->configFactory->get($name)->get($key); |
|
} |
|
|
|
/** |
|
* Evaluates a string of PHP code. |
|
* |
|
* @param string $code |
|
* Valid PHP code to be evaluated. |
|
* |
|
* @return mixed |
|
* The eval() result. |
|
*/ |
|
public function phpFilter($code) { |
|
ob_start(); |
|
// @codingStandardsIgnoreStart |
|
print eval($code); |
|
// @codingStandardsIgnoreEnd |
|
$output = ob_get_contents(); |
|
ob_end_clean(); |
|
return $output; |
|
} |
|
|
|
/** |
|
* Replaces all tokens in a given string with appropriate values. |
|
* |
|
* @param string $text |
|
* An HTML string containing replaceable tokens. |
|
* |
|
* @return string |
|
* The entered HTML text with tokens replaced. |
|
*/ |
|
public function tokenReplaceFilter($text) { |
|
return $this->token->replace($text); |
|
} |
|
|
|
/** |
|
* Performs a regular expression search and replace. |
|
* |
|
* @param string $text |
|
* The text to search and replace. |
|
* @param string $pattern |
|
* The pattern to search for. |
|
* @param string $replacement |
|
* The string to replace. |
|
* |
|
* @return string |
|
* The new text if matches are found, otherwise unchanged text. |
|
*/ |
|
public function pregReplaceFilter($text, $pattern, $replacement) { |
|
return preg_replace("/$pattern/", $replacement, $text); |
|
} |
|
|
|
/** |
|
* Returns the URL of this image derivative for an original image path or URI. |
|
* |
|
* @param string $path |
|
* The path or URI to the original image. |
|
* @param string $style |
|
* The image style. |
|
* |
|
* @return string |
|
* The absolute URL where a style image can be downloaded, suitable for use |
|
* in an <img> tag. Requesting the URL will cause the image to be created. |
|
*/ |
|
public function imageStyle($path, $style) { |
|
$url = ImageStyle::load($style)->buildUrl($path); |
|
return file_url_transform_relative($url); |
|
} |
|
|
|
/** |
|
* Sets a message to display to the user. |
|
* |
|
* @param string|\Drupal\Component\Render\MarkupInterface $message |
|
* (optional) The translated message to be displayed to the user. |
|
* @param string $type |
|
* (optional) The message's type. Defaults to 'status'. |
|
* @param bool $repeat |
|
* (optional) If this is FALSE and the message is already set, then the |
|
* message won't be repeated. Defaults to FALSE. |
|
* |
|
* @see drupal_set_message() |
|
*/ |
|
public function drupalSetMessage($message = NULL, $type = 'status', $repeat = FALSE) { |
|
drupal_set_message($message, $type, $repeat); |
|
} |
|
|
|
}
|
|
|