Skip to content

Commit

Permalink
Merge pull request #257 from moreonion/layout-context
Browse files Browse the repository at this point in the history
[layout] add context conditions
  • Loading branch information
torotil authored Dec 17, 2020
2 parents b3887a6 + e18bbe5 commit e6d05b7
Show file tree
Hide file tree
Showing 7 changed files with 209 additions and 31 deletions.
46 changes: 43 additions & 3 deletions campaignion_layout/campaignion_layout.module
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
use Drupal\campaignion_layout\Entity;
use Drupal\campaignion_layout\Lookup;
use Drupal\campaignion_layout\Themes;
use Drupal\campaignion_layout\Context\LayoutCondition;
use Drupal\campaignion_layout\Context\ThemeCondition;
use Drupal\little_helpers\Services\Container;

// The field.module does not group its hooks so we have to include this here.
Expand Down Expand Up @@ -66,17 +68,31 @@ function campaignion_layout_little_helpers_services() {
return $info;
}

/**
* Implements hook_page_build().
*/
function campaignion_layout_page_build(&$page, $node = NULL) {
$node = $node ?? menu_get_object();
if ($node && ($layout = Lookup::fromEntity('node', $node)->getLayout())) {
$page['#layout'] = $layout;
if (module_exists('context')) {
if ($plugin = context_get_plugin('condition', 'campaignion_layout_context_condition_layout')) {
$plugin->execute($layout['name']);
}
}
}
}

/**
* Prepare variables for page templates.
*/
function campaignion_layout_preprocess_page(array &$vars) {
$vars['layout'] = NULL;
if (($node = $vars['node'] ?? NULL) && ($layout = Lookup::fromEntity('node', $node)->getLayout())) {
if ($layout = $vars['page']['#layout'] ?? NULL) {
$vars['layout'] = $layout['name'];
array_unshift($vars['theme_hook_suggestions'], "page__layout__{$layout['name']}");

foreach ($layout['fields'] as $field_name => $settings) {
$vars[$settings['variable']] = field_view_field('node', $node, $field_name, $settings['display']);
$vars[$settings['variable']] = field_view_field('node', $vars['node'], $field_name, $settings['display']);
}
}
}
Expand Down Expand Up @@ -180,3 +196,27 @@ function campaignion_layout_node_prepare($node) {
}
}
}

/**
* Impelements hook_context_plugins().
*/
function campaignion_layout_context_plugins() {
$plugins['campaignion_layout_context_condition_layout']['handler']['class'] = LayoutCondition::class;
return $plugins;
}

/**
* Implements hook_context_registry().
*/
function campaignion_layout_context_registry() {
$registry = [
'conditions' => [
'campaignion_layout_context_condition_layout' => [
'title' => t('Layout'),
'description' => t('Set this context based on the selected layout.'),
'plugin' => 'campaignion_layout_context_condition_layout',
],
],
];
return $registry;
}
32 changes: 32 additions & 0 deletions campaignion_layout/src/Context/LayoutCondition.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php

namespace Drupal\campaignion_layout\Context;

use Drupal\little_helpers\Services\Container;
use Drupal\campaignion_layout\Lookup;

/**
* Expose available layouts as a context condition.
*/
class LayoutCondition extends \context_condition {

/**
* Condition values.
*/
public function condition_values() {
return Container::get()->loadService('campaignion_layout.themes')->layoutOptions();
}

/**
* Check whether the condition is met.
*
* @param string $active_layout_name
* Machine name of the active layout if one was set.
*/
public function execute(string $active_layout_name) {
foreach ($this->get_contexts($active_layout_name) as $context) {
$this->condition_met($context, $active_layout_name);
}
}

}
71 changes: 71 additions & 0 deletions campaignion_layout/tests/Context/LayoutConditionTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
<?php

namespace Drupal\campaignion_layout\Context;

use Drupal\campaignion_layout\Tests\ThemesBaseTest;

/**
* Test for the layout context condition implementation.
*/
class LayoutConditionTest extends ThemesBaseTest {

/**
* Test the available form options for the context condition plugin.
*/
public function testFormOptions() {
$themes['foo']['title'] = 'Foo';
$themes['foo']['layouts']['1col']['title'] = 'Single column';
$themes['bar']['title'] = 'Bar';
$themes['bar']['layouts']['2col']['title'] = 'Two columns';
$this->injectThemes($themes);
$condition = new LayoutCondition('plugin', []);
$this->assertEqual([
'1col' => 'Single column',
'2col' => 'Two columns',
], $condition->condition_values());
}

/**
* Test the behaviour of the execute function.
*/
public function testExecuteWithMultipleContexts() {
$mock_condition = $this->getMockBuilder(LayoutCondition::class)
->disableOriginalConstructor()
->setMethods(['condition_met', 'get_contexts'])
->getMock();
$active_layout = 'active_layout';
$matching_contexts = ['one', 'two', 'three'];
$mock_condition->expects($this->once())
->method('get_contexts')
->with($active_layout)
->willReturn($matching_contexts);
$mock_condition->expects($this->exactly(3))
->method('condition_met')
->withConsecutive(
['one', $active_layout],
['two', $active_layout],
['three', $active_layout]
);
$mock_condition->execute($active_layout);
}

/**
* Test the behaviour of the execute function.
*/
public function testExecuteWithNoContexts() {
$mock_condition = $this->getMockBuilder(LayoutCondition::class)
->disableOriginalConstructor()
->setMethods(['condition_met', 'get_contexts'])
->getMock();
$theme = 'theme_name';
$matching_contexts = [];
$mock_condition->expects($this->once())
->method('get_contexts')
->with('theme_name')
->willReturn($matching_contexts);
$mock_condition->expects($this->exactly(0))
->method('condition_met');
$mock_condition->execute($theme);
}

}
2 changes: 2 additions & 0 deletions campaignion_layout/tests/FieldIntegrationTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ public function testNodePreprocess() {
'uri' => '/misc/druplicon.png',
];
$vars['theme_hook_suggestions'] = [];
$vars['page'] = [];
$theme = $this->injectTheme(TRUE);
$theme->method('isActive')->willReturn(TRUE);
$theme->method('getLayout')->willReturn([
Expand All @@ -67,6 +68,7 @@ public function testNodePreprocess() {
],
],
]);
campaignion_layout_page_build($vars['page'], $vars['node']);
campaignion_layout_preprocess_page($vars);
$this->assertEqual('foo', $vars['layout']);
$this->assertEqual(['page__layout__foo'], $vars['theme_hook_suggestions']);
Expand Down
31 changes: 3 additions & 28 deletions campaignion_layout/tests/FieldTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,46 +2,21 @@

namespace Drupal\campaignion_layout;

use Drupal\little_helpers\Services\Container;
use Upal\DrupalUnitTestCase;
use Drupal\campaignion_layout\Tests\ThemesBaseTest;

/**
* Test for the field integration.
*/
class FieldTest extends DrupalUnitTestCase {
class FieldTest extends ThemesBaseTest {

/**
* Clean up the injected services.
* Clean up html IDs.
*/
public function tearDown() : void {
Container::get()->inject('campaignion_layout.themes', NULL);
drupal_static_reset('drupal_html_id');
parent::tearDown();
}

/**
* Inject a themes service with specific metadata.
*/
protected function injectThemes($themes = [], $layouts = []) {
$theme_objects = [];
$add_layout_defaults = function ($info) {
return $info + ['fields' => []];
};
foreach ($themes as $name => $data) {
$theme = $this->createMock(Theme::class);
$theme->method('title')->willReturn($data['title'] ?? $name);
$theme->method('layouts')
->willReturn(array_map($add_layout_defaults, $data['layouts']));
$theme_objects[$name] = $theme;
$layouts += $data['layouts'];
}
$themes = $this->createMock(Themes::class);
$themes->method('enabledThemes')->willReturn($theme_objects);
$themes->method('declaredLayouts')
->willReturn(array_map($add_layout_defaults, $layouts));
Container::get()->inject('campaignion_layout.themes', $themes);
}

/**
* Generate theme test data.
*/
Expand Down
57 changes: 57 additions & 0 deletions campaignion_layout/tests/src/ThemesBaseTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<?php

namespace Drupal\campaignion_layout\Tests;

use Drupal\little_helpers\Services\Container;
use Upal\DrupalUnitTestCase;

use Drupal\campaignion_layout\Theme;
use Drupal\campaignion_layout\Themes;

/**
* Base test for testing theme & layout handling.
*
* The test class provides a simple way of injecting a Themes service with
* themes and layouts configured.
*/
abstract class ThemesBaseTest extends DrupalUnitTestCase {

/**
* Cleanup the injected service.
*/
public function tearDown() : void {
Container::get()->inject('campaignion_layout.themes', NULL);
parent::tearDown();
}

/**
* Inject a themes service with specific theme and layout data.
*/
protected function injectThemes(array $themes = [], array $layouts = []) {
$theme_objects = [];
$add_layout_defaults = function ($info) {
return $info + ['fields' => []];
};
foreach ($themes as $name => $data) {
$data += ['layouts' => []];
$theme = $this->getMockBuilder(Theme::class)
->disableOriginalConstructor()
->setMethods(['title', 'layouts'])
->getMock();
$theme->method('title')->willReturn($data['title'] ?? $name);
$theme->method('layouts')
->willReturn(array_map($add_layout_defaults, $data['layouts']));
$theme_objects[$name] = $theme;
$layouts += $data['layouts'];
}
$themes = $this->getMockBuilder(Themes::class)
->disableOriginalConstructor()
->setMethods(['enabledThemes', 'declaredLayouts'])
->getMock();
$themes->method('enabledThemes')->willReturn($theme_objects);
$themes->method('declaredLayouts')
->willReturn(array_map($add_layout_defaults, $layouts));
Container::get()->inject('campaignion_layout.themes', $themes);
}

}
1 change: 1 addition & 0 deletions campaignion_test/campaignion_test.info
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ dependencies[] = campaignion:campaignion_tracking
dependencies[] = campaignion:campaignion_tracking_gtm
dependencies[] = campaignion:campaignion_webform_tokens
dependencies[] = campaignion:campaignion_wizard
dependencies[] = context:context
dependencies[] = ctools:ctools
dependencies[] = date:date
dependencies[] = drupal:image
Expand Down

0 comments on commit e6d05b7

Please sign in to comment.