From b081d525925fdc9c6d262b34eb7fbcaabffc6c65 Mon Sep 17 00:00:00 2001 From: Andy Broomfield Date: Sat, 5 Jun 2021 00:14:03 +0100 Subject: [PATCH 1/3] Add layout builder support for service blocks Ref #116 Add the node context to the service blocks, this allows them to be used with layout builder --- .../Block/ServicesRelatedLinksBlock.php | 12 +++++++---- .../Block/ServicesRelatedTopicsBlock.php | 8 +++++-- src/Plugin/Block/ServicesBlockBase.php | 21 +++++++++++++++++-- src/Plugin/Block/ServicesCtaBlock.php | 10 +++++++++ 4 files changed, 43 insertions(+), 8 deletions(-) diff --git a/modules/localgov_services_page/src/Plugin/Block/ServicesRelatedLinksBlock.php b/modules/localgov_services_page/src/Plugin/Block/ServicesRelatedLinksBlock.php index fde2f17..9c7efac 100644 --- a/modules/localgov_services_page/src/Plugin/Block/ServicesRelatedLinksBlock.php +++ b/modules/localgov_services_page/src/Plugin/Block/ServicesRelatedLinksBlock.php @@ -16,6 +16,9 @@ * @Block( * id = "localgov_services_related_links_block", * admin_label = @Translation("Service page related links"), + * context_definitions = { + * "node" = @ContextDefinition("entity:node", label = @Translation("Node")) + * } * ) */ class ServicesRelatedLinksBlock extends ServicesBlockBase implements ContainerFactoryPluginInterface { @@ -24,7 +27,8 @@ class ServicesRelatedLinksBlock extends ServicesBlockBase implements ContainerFa * {@inheritdoc} */ public function build() { - $build = []; + // $this->node = $this->getContextValue('node'); + $build = parent::build(); $links = $this->getShouldUseManual() ? $this->buildManual() : $this->buildAutomated(); @@ -47,7 +51,7 @@ public function build() { private function buildManual() { $links = []; - if ($this->node->hasField('localgov_related_links')) { + if ($this->node && $this->node->hasField('localgov_related_links')) { foreach ($this->node->get('localgov_related_links')->getValue() as $link) { if (isset($link['title']) && isset($link['uri'])) { $links[] = [ @@ -120,7 +124,7 @@ private function buildAutomated() { * Should manual links be displayed? */ private function getShouldUseManual() { - if ($this->node->hasField('localgov_override_related_links') && !$this->node->get('localgov_override_related_links')->isEmpty()) { + if ($this->node && $this->node->hasField('localgov_override_related_links') && !$this->node->get('localgov_override_related_links')->isEmpty()) { return $this->node->get('localgov_override_related_links')->first()->getValue()['value']; } @@ -136,7 +140,7 @@ private function getShouldUseManual() { private function getTopics() { $topics = []; - if ($this->node->hasField('localgov_topic_classified')) { + if ($this->node && $this->node->hasField('localgov_topic_classified')) { /** @var \Drupal\taxonomy\TermInterface $term_info */ foreach ($this->node->get('localgov_topic_classified')->getValue() as $term_info) { diff --git a/modules/localgov_services_page/src/Plugin/Block/ServicesRelatedTopicsBlock.php b/modules/localgov_services_page/src/Plugin/Block/ServicesRelatedTopicsBlock.php index 38fa650..f36c338 100644 --- a/modules/localgov_services_page/src/Plugin/Block/ServicesRelatedTopicsBlock.php +++ b/modules/localgov_services_page/src/Plugin/Block/ServicesRelatedTopicsBlock.php @@ -14,6 +14,9 @@ * @Block( * id = "localgov_services_related_topics_block", * admin_label = @Translation("Service page related topics"), + * context_definitions = { + * "node" = @ContextDefinition("entity:node", label = @Translation("Node")) + * } * ) */ class ServicesRelatedTopicsBlock extends ServicesBlockBase { @@ -22,10 +25,11 @@ class ServicesRelatedTopicsBlock extends ServicesBlockBase { * {@inheritdoc} */ public function build() { - $build = []; + // $this->node = $this->getContextValue('node'); + $build = parent::build(); $links = []; - if ($this->node->hasField('localgov_topic_classified')) { + if ($this->node && $this->node->hasField('localgov_topic_classified')) { /** @var \Drupal\taxonomy\TermInterface $term_info */ foreach ($this->node->get('localgov_topic_classified')->getValue() as $term_info) { $term = Term::load($term_info['target_id']); diff --git a/src/Plugin/Block/ServicesBlockBase.php b/src/Plugin/Block/ServicesBlockBase.php index ede7685..9759211 100644 --- a/src/Plugin/Block/ServicesBlockBase.php +++ b/src/Plugin/Block/ServicesBlockBase.php @@ -62,7 +62,7 @@ public function __construct(array $configuration, $plugin_id, $plugin_definition if ($this->routeMatch->getParameter('node')) { $this->node = $this->routeMatch->getParameter('node'); - if (!$this->node instanceof NodeInterface) { + if (!$this->node instanceof NodeInterface && is_int($this->node)) { $node_storage = $this->entityTypeManager->getStorage('node'); $this->node = $node_storage->load($this->node); } @@ -89,11 +89,28 @@ protected function blockAccess(AccountInterface $account) { return AccessResult::allowedIf($this->node); } + /** + * {@inheritdoc} + */ + public function build() { + + // If there is a node context, set its value. + if ($node_context = $this->getContextValue('node')) { + $this->node = $node_context; + } + + return []; + } + /** * {@inheritdoc} */ public function getCacheTags() { - return Cache::mergeTags(parent::getCacheTags(), ['node:' . $this->node->id()]); + if ($this->node instanceof NodeInterface) { + return Cache::mergeTags(parent::getCacheTags(), ['node:' . $this->node->id()]); + } else { + return parent::getCacheTags(); + } } /** diff --git a/src/Plugin/Block/ServicesCtaBlock.php b/src/Plugin/Block/ServicesCtaBlock.php index af102ac..8f26137 100644 --- a/src/Plugin/Block/ServicesCtaBlock.php +++ b/src/Plugin/Block/ServicesCtaBlock.php @@ -5,6 +5,7 @@ use Drupal\Core\Access\AccessResult; use Drupal\Core\Session\AccountInterface; use Drupal\Core\Url; +use Drupal\Core\Plugin\ContainerFactoryPluginInterface; /** * Provides a 'Services CTA Block' block. @@ -14,6 +15,9 @@ * @Block( * id = "localgov_service_cta_block", * admin_label = @Translation("Services call to action"), + * context_definitions = { + * "node" = @ContextDefinition("entity:node", label = @Translation("Node")) + * } * ) */ class ServicesCtaBlock extends ServicesBlockBase { @@ -36,7 +40,13 @@ protected function blockAccess(AccountInterface $account) { * {@inheritdoc} */ public function build() { + // $this->node = $this->getContextValue('node'); + parent::build(); + $buttons = []; + if (empty($this->node)) { + return []; + } foreach ($this->node->get('localgov_common_tasks')->getValue() as $call_to_action) { $type = 'cta-info'; From 8b4547c505aeb3701c409ea5a90b8d354fcf367d Mon Sep 17 00:00:00 2001 From: Andy Broomfield Date: Sun, 10 Dec 2023 15:16:08 +0000 Subject: [PATCH 2/3] Update all services blocks to use node contexts Set a node context to all service blocks and update the block base and all service block functions to use the contextValue('node') if present. Update each of the provided blocks to set a context_mapping for the current node route Provide update hooks for CTA, releated links and related topics blocks Provide a backward compatible way of accessing the current node via $this->node for blocks that have not updated to use a node context so they can still get the current node. (todo: decide if this should then be deprecated). --- ....localgov_servicepagerelatedlinks_base.yml | 2 ++ ...algov_servicepagerelatedlinks_scarfolk.yml | 2 ++ ...localgov_servicepagerelatedtopics_base.yml | 2 ++ ...lgov_servicepagerelatedtopics_scarfolk.yml | 2 ++ ...ock.localgov_servicescalltoaction_base.yml | 2 ++ ...localgov_servicescalltoaction_scarfolk.yml | 2 ++ localgov_services.install | 24 ++++++++++++++ .../localgov_services_page.install | 26 +++++++++++++++- .../Block/ServicesRelatedLinksBlock.php | 25 ++++++++------- .../Block/ServicesRelatedTopicsBlock.php | 15 ++++----- src/Plugin/Block/ServicesBlockBase.php | 31 +++++++++---------- src/Plugin/Block/ServicesCtaBlock.php | 20 ++++++------ 12 files changed, 106 insertions(+), 47 deletions(-) create mode 100644 localgov_services.install diff --git a/config/optional/block.block.localgov_servicepagerelatedlinks_base.yml b/config/optional/block.block.localgov_servicepagerelatedlinks_base.yml index 8e57ecf..e3317ae 100644 --- a/config/optional/block.block.localgov_servicepagerelatedlinks_base.yml +++ b/config/optional/block.block.localgov_servicepagerelatedlinks_base.yml @@ -16,4 +16,6 @@ settings: label: 'Service page related links' provider: localgov_services_page label_display: '0' + context_mapping: + node: '@node.node_route_context:node' visibility: { } diff --git a/config/optional/block.block.localgov_servicepagerelatedlinks_scarfolk.yml b/config/optional/block.block.localgov_servicepagerelatedlinks_scarfolk.yml index 4a77a5f..aad3139 100644 --- a/config/optional/block.block.localgov_servicepagerelatedlinks_scarfolk.yml +++ b/config/optional/block.block.localgov_servicepagerelatedlinks_scarfolk.yml @@ -16,4 +16,6 @@ settings: label: 'Service page related links' provider: localgov_services_page label_display: '0' + context_mapping: + node: '@node.node_route_context:node' visibility: { } diff --git a/config/optional/block.block.localgov_servicepagerelatedtopics_base.yml b/config/optional/block.block.localgov_servicepagerelatedtopics_base.yml index 38e5d8f..e5d3a19 100644 --- a/config/optional/block.block.localgov_servicepagerelatedtopics_base.yml +++ b/config/optional/block.block.localgov_servicepagerelatedtopics_base.yml @@ -16,4 +16,6 @@ settings: label: 'Service page related topics' provider: localgov_services_page label_display: '0' + context_mapping: + node: '@node.node_route_context:node' visibility: { } diff --git a/config/optional/block.block.localgov_servicepagerelatedtopics_scarfolk.yml b/config/optional/block.block.localgov_servicepagerelatedtopics_scarfolk.yml index 656346f..cb03b5f 100644 --- a/config/optional/block.block.localgov_servicepagerelatedtopics_scarfolk.yml +++ b/config/optional/block.block.localgov_servicepagerelatedtopics_scarfolk.yml @@ -16,4 +16,6 @@ settings: label: 'Service page related topics' provider: localgov_services_page label_display: '0' + context_mapping: + node: '@node.node_route_context:node' visibility: { } diff --git a/config/optional/block.block.localgov_servicescalltoaction_base.yml b/config/optional/block.block.localgov_servicescalltoaction_base.yml index 4f86819..360ee7d 100644 --- a/config/optional/block.block.localgov_servicescalltoaction_base.yml +++ b/config/optional/block.block.localgov_servicescalltoaction_base.yml @@ -16,4 +16,6 @@ settings: label: 'Services call to action' provider: localgov_services label_display: '0' + context_mapping: + node: '@node.node_route_context:node' visibility: { } diff --git a/config/optional/block.block.localgov_servicescalltoaction_scarfolk.yml b/config/optional/block.block.localgov_servicescalltoaction_scarfolk.yml index b413f27..9864d88 100644 --- a/config/optional/block.block.localgov_servicescalltoaction_scarfolk.yml +++ b/config/optional/block.block.localgov_servicescalltoaction_scarfolk.yml @@ -16,4 +16,6 @@ settings: label: 'Services call to action' provider: localgov_services label_display: '0' + context_mapping: + node: '@node.node_route_context:node' visibility: { } diff --git a/localgov_services.install b/localgov_services.install new file mode 100644 index 0000000..1e3a70f --- /dev/null +++ b/localgov_services.install @@ -0,0 +1,24 @@ +getStorage('block')->loadByProperties([ + 'plugin' => 'localgov_service_cta_block', + ]); + + foreach ($cta_blocks as $block) { + $settings = $block->get('settings'); + // Modify settings. + $settings['context_mapping']['node'] = '@node.node_route_context:node'; + $block->set('settings', $settings); + $block->save(); + } +} diff --git a/modules/localgov_services_page/localgov_services_page.install b/modules/localgov_services_page/localgov_services_page.install index 2ecd953..b008c94 100644 --- a/modules/localgov_services_page/localgov_services_page.install +++ b/modules/localgov_services_page/localgov_services_page.install @@ -2,7 +2,7 @@ /** * @file - * LocalGov services page subanding install file. + * LocalGov services page install file. */ use Drupal\localgov_core\FieldRenameHelper; @@ -35,3 +35,27 @@ function localgov_services_page_update_8001(&$sandbox) { return t('Please export your sites configuration! Config entities for localgov_services_page where updated.'); } + +/** + * Add node_route context mapping to service page blocks. + * + * Adding to localgov_services_related_links_block and + * localgov_services_related_topics_block. + */ +function localgov_services_page_update_8002() { + $entity_type_manager = \Drupal::entityTypeManager(); + $related_links_blocks = $entity_type_manager->getStorage('block')->loadByProperties([ + 'plugin' => 'localgov_services_related_links_block', + ]); + $related_topics_blocks = $entity_type_manager->getStorage('block')->loadByProperties([ + 'plugin' => 'localgov_services_related_topics_block', + ]); + + foreach (array_merge($related_links_blocks, $related_topics_blocks) as $block) { + $settings = $block->get('settings'); + // Modify settings. + $settings['context_mapping']['node'] = '@node.node_route_context:node'; + $block->set('settings', $settings); + $block->save(); + } +} diff --git a/modules/localgov_services_page/src/Plugin/Block/ServicesRelatedLinksBlock.php b/modules/localgov_services_page/src/Plugin/Block/ServicesRelatedLinksBlock.php index 9c7efac..de508fe 100644 --- a/modules/localgov_services_page/src/Plugin/Block/ServicesRelatedLinksBlock.php +++ b/modules/localgov_services_page/src/Plugin/Block/ServicesRelatedLinksBlock.php @@ -17,7 +17,7 @@ * id = "localgov_services_related_links_block", * admin_label = @Translation("Service page related links"), * context_definitions = { - * "node" = @ContextDefinition("entity:node", label = @Translation("Node")) + * "node" = @ContextDefinition("entity:node", label = @Translation("Node"), required = TRUE) * } * ) */ @@ -27,9 +27,7 @@ class ServicesRelatedLinksBlock extends ServicesBlockBase implements ContainerFa * {@inheritdoc} */ public function build() { - // $this->node = $this->getContextValue('node'); - $build = parent::build(); - + $build = []; $links = $this->getShouldUseManual() ? $this->buildManual() : $this->buildAutomated(); if ($links) { @@ -50,9 +48,10 @@ public function build() { */ private function buildManual() { $links = []; + $node = $this->getContextValue('node'); - if ($this->node && $this->node->hasField('localgov_related_links')) { - foreach ($this->node->get('localgov_related_links')->getValue() as $link) { + if ($node && $node->hasField('localgov_related_links')) { + foreach ($node->get('localgov_related_links')->getValue() as $link) { if (isset($link['title']) && isset($link['uri'])) { $links[] = [ 'title' => $link['title'], @@ -86,6 +85,8 @@ private function buildAutomated() { } if ($topics) { + $this_node = $this->getContextValue('node'); + // Perform our query. $query = $this->database->query('SELECT entity_id FROM node__localgov_topic_classified LEFT JOIN node_field_data ON node_field_data.nid=node__localgov_topic_classified.entity_id @@ -96,7 +97,7 @@ private function buildAutomated() { ORDER BY count(*) desc LIMIT 6;', [ - ':nid' => $this->node->id(), + ':nid' => $this_node->id(), ':tids[]' => $topics, ] ); @@ -124,8 +125,9 @@ private function buildAutomated() { * Should manual links be displayed? */ private function getShouldUseManual() { - if ($this->node && $this->node->hasField('localgov_override_related_links') && !$this->node->get('localgov_override_related_links')->isEmpty()) { - return $this->node->get('localgov_override_related_links')->first()->getValue()['value']; + $node = $this->getContextValue('node'); + if ($node && $node->hasField('localgov_override_related_links') && !$node->get('localgov_override_related_links')->isEmpty()) { + return $node->get('localgov_override_related_links')->first()->getValue()['value']; } return FALSE; @@ -139,11 +141,12 @@ private function getShouldUseManual() { */ private function getTopics() { $topics = []; + $node = $this->getContextValue('node'); - if ($this->node && $this->node->hasField('localgov_topic_classified')) { + if ($node && $node->hasField('localgov_topic_classified')) { /** @var \Drupal\taxonomy\TermInterface $term_info */ - foreach ($this->node->get('localgov_topic_classified')->getValue() as $term_info) { + foreach ($node->get('localgov_topic_classified')->getValue() as $term_info) { $topicEntity = $this->entityTypeManager->getStorage('taxonomy_term')->load($term_info['target_id']); // Add topic only if an actual taxonomy term, diff --git a/modules/localgov_services_page/src/Plugin/Block/ServicesRelatedTopicsBlock.php b/modules/localgov_services_page/src/Plugin/Block/ServicesRelatedTopicsBlock.php index f36c338..c903ea3 100644 --- a/modules/localgov_services_page/src/Plugin/Block/ServicesRelatedTopicsBlock.php +++ b/modules/localgov_services_page/src/Plugin/Block/ServicesRelatedTopicsBlock.php @@ -15,7 +15,7 @@ * id = "localgov_services_related_topics_block", * admin_label = @Translation("Service page related topics"), * context_definitions = { - * "node" = @ContextDefinition("entity:node", label = @Translation("Node")) + * "node" = @ContextDefinition("entity:node", label = @Translation("Node"), required = TRUE) * } * ) */ @@ -25,13 +25,13 @@ class ServicesRelatedTopicsBlock extends ServicesBlockBase { * {@inheritdoc} */ public function build() { - // $this->node = $this->getContextValue('node'); - $build = parent::build(); + $build = []; $links = []; + $node = $this->getContextValue('node'); - if ($this->node && $this->node->hasField('localgov_topic_classified')) { + if ($node && $node->hasField('localgov_topic_classified')) { /** @var \Drupal\taxonomy\TermInterface $term_info */ - foreach ($this->node->get('localgov_topic_classified')->getValue() as $term_info) { + foreach ($node->get('localgov_topic_classified')->getValue() as $term_info) { $term = Term::load($term_info['target_id']); // Add link only if an actual taxonomy term, @@ -64,8 +64,9 @@ public function build() { * @throws \Drupal\Core\TypedData\Exception\MissingDataException */ private function hideRelatedTopics() { - if ($this->node->hasField('localgov_hide_related_topics') && !$this->node->get('localgov_hide_related_topics')->isEmpty()) { - return (bool) $this->node->get('localgov_hide_related_topics')->first()->getValue()['value']; + $node = $this->getContextValue('node'); + if ($node->hasField('localgov_hide_related_topics') && !$node->get('localgov_hide_related_topics')->isEmpty()) { + return (bool) $node->get('localgov_hide_related_topics')->first()->getValue()['value']; } return FALSE; diff --git a/src/Plugin/Block/ServicesBlockBase.php b/src/Plugin/Block/ServicesBlockBase.php index 9759211..28771d2 100644 --- a/src/Plugin/Block/ServicesBlockBase.php +++ b/src/Plugin/Block/ServicesBlockBase.php @@ -22,7 +22,10 @@ abstract class ServicesBlockBase extends BlockBase implements ContainerFactoryPl /** * Service node instance. * + * This is a backup from the current route if no node context is present. + * * @var \Drupal\node\Entity\Node + * @todo deprecate this property. */ protected $node = FALSE; @@ -60,6 +63,10 @@ public function __construct(array $configuration, $plugin_id, $plugin_definition $this->routeMatch = $route_match; $this->entityTypeManager = $entity_type_manager; + // Set the node property to the node in the current route. + // This was the previous way of getting the current node if a node context + // has not been set. It's presence is for backward compatability with + // blocks that extend this block that have not been updated. if ($this->routeMatch->getParameter('node')) { $this->node = $this->routeMatch->getParameter('node'); if (!$this->node instanceof NodeInterface && is_int($this->node)) { @@ -86,29 +93,19 @@ public static function create(ContainerInterface $container, array $configuratio * {@inheritdoc} */ protected function blockAccess(AccountInterface $account) { - return AccessResult::allowedIf($this->node); - } - - /** - * {@inheritdoc} - */ - public function build() { - - // If there is a node context, set its value. - if ($node_context = $this->getContextValue('node')) { - $this->node = $node_context; - } - - return []; + $node = isset($this->getContextDefinitions()['node']) ? $this->getContextValue('node') : $this->node; + return AccessResult::allowedIf($node); } /** * {@inheritdoc} */ public function getCacheTags() { - if ($this->node instanceof NodeInterface) { - return Cache::mergeTags(parent::getCacheTags(), ['node:' . $this->node->id()]); - } else { + $node = isset($this->getContextDefinitions()['node']) ? $this->getContextValue('node') : $this->node; + if ($node instanceof NodeInterface) { + return Cache::mergeTags(parent::getCacheTags(), ['node:' . $node->id()]); + } + else { return parent::getCacheTags(); } } diff --git a/src/Plugin/Block/ServicesCtaBlock.php b/src/Plugin/Block/ServicesCtaBlock.php index 8f26137..79bff0b 100644 --- a/src/Plugin/Block/ServicesCtaBlock.php +++ b/src/Plugin/Block/ServicesCtaBlock.php @@ -5,7 +5,6 @@ use Drupal\Core\Access\AccessResult; use Drupal\Core\Session\AccountInterface; use Drupal\Core\Url; -use Drupal\Core\Plugin\ContainerFactoryPluginInterface; /** * Provides a 'Services CTA Block' block. @@ -16,7 +15,7 @@ * id = "localgov_service_cta_block", * admin_label = @Translation("Services call to action"), * context_definitions = { - * "node" = @ContextDefinition("entity:node", label = @Translation("Node")) + * "node" = @ContextDefinition("entity:node", label = @Translation("Node"), required = TRUE) * } * ) */ @@ -27,9 +26,10 @@ class ServicesCtaBlock extends ServicesBlockBase { */ protected function blockAccess(AccountInterface $account) { // We only show this block if the current node contains some CTA actions. - if ($this->node && - $this->node->hasField('localgov_common_tasks') && - count($this->node->get('localgov_common_tasks')->getValue()) >= 1 + $node = $this->getContextValue('node'); + if ($node && + $node->hasField('localgov_common_tasks') && + count($node->get('localgov_common_tasks')->getValue()) >= 1 ) { return AccessResult::allowed(); } @@ -40,15 +40,13 @@ protected function blockAccess(AccountInterface $account) { * {@inheritdoc} */ public function build() { - // $this->node = $this->getContextValue('node'); - parent::build(); - $buttons = []; - if (empty($this->node)) { + $node = $this->getContextValue('node'); + if (empty($node)) { return []; } - foreach ($this->node->get('localgov_common_tasks')->getValue() as $call_to_action) { + foreach ($node->get('localgov_common_tasks')->getValue() as $call_to_action) { $type = 'cta-info'; if (isset($call_to_action['options']['type']) && $call_to_action['options']['type'] === 'action') { $type = 'cta-action'; @@ -67,7 +65,7 @@ public function build() { '#theme' => 'services_cta_block', '#buttons' => $buttons, '#cache' => [ - 'tags' => ['node:' . $this->node->id()], + 'tags' => ['node:' . $node->id()], 'contexts' => ['url.path'], ], ]; From 169c49831590c39021ca2bb4df00840c8494c064 Mon Sep 17 00:00:00 2001 From: Andy Broomfield Date: Wed, 10 Dec 2025 11:49:29 +0000 Subject: [PATCH 3/3] Add layout builder support to the service landing template in the module Check for layout builder support and if present render the layout builder instead --- .../node--localgov-services-landing--full.html.twig | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/modules/localgov_services_landing/templates/node--localgov-services-landing--full.html.twig b/modules/localgov_services_landing/templates/node--localgov-services-landing--full.html.twig index 98af977..c6b4bd3 100644 --- a/modules/localgov_services_landing/templates/node--localgov-services-landing--full.html.twig +++ b/modules/localgov_services_landing/templates/node--localgov-services-landing--full.html.twig @@ -17,6 +17,11 @@ ] %} +{# Render layout builder as is idf present #} +{% if content._layout_builder|render|trim is not empty %} + {{ content._layout_builder }} +{% else %} + {% if content.localgov_destinations %}

{{ 'Services List'|t }}

@@ -109,3 +114,4 @@ +{% endif %}