diff --git a/render_example/css/render_example.css b/render_example/css/render_example.css new file mode 100644 index 0000000..0da7f30 --- /dev/null +++ b/render_example/css/render_example.css @@ -0,0 +1,20 @@ +.render-array { + border: 2px solid #000; + margin-top: 10px; + padding-left: 5px; + padding-top: 5px; +} + +.render-header { + font-size: large; + font-style: italic; +} + +.unrendered-label { + font-style: italic; + margin-top: 10px; +} + +.rendered { + background-color: #add8e6; +} diff --git a/render_example/lib/Drupal/render_example/Tests/RenderExampleTest.php b/render_example/lib/Drupal/render_example/Tests/RenderExampleTest.php deleted file mode 100644 index a5d7c74..0000000 --- a/render_example/lib/Drupal/render_example/Tests/RenderExampleTest.php +++ /dev/null @@ -1,135 +0,0 @@ - 'Render example functionality', - 'description' => 'Test Render Example', - 'group' => 'Examples', - // @todo Figure out if this is used, inherited from D7. - 'dependencies' => array('devel'), - ); - } - - /** - * Assert that all of the xpaths in the array have results. - * - * @param $xpath_array - * An array of xpaths, each of which must return something. - */ - function assertRenderResults($xpath_array) { - foreach ($xpath_array as $xpath) { - $result = $this->xpath($xpath); - $this->assertTrue(!empty($result), format_string('Found xpath %xpath', array('%xpath' => $xpath))); - } - } - - /** - * Asserts that the string value of the result is the same as the passed text. - * - * @param $xpath_array - * Array of keyed arrays of tests to be made. Each child array consists of - * $xpath => $expected_text - */ - function assertRenderedText($xpath_array) { - foreach ($xpath_array as $xpath => $text) { - $result = $this->xpath($xpath); - $this->assertTrue((string)$result[0][0] == $text, format_string('%ary selects text %text', array('%ary' => $xpath, '%text' => $text))); - } - } - - /** - * Login user, create an example node, and test blog functionality through the admin and user interfaces. - */ - function testRenderExampleBasic() { - // Create a user that can access devel information and log in. - $web_user = $this->drupalCreateUser(array('access devel information', 'access content')); - $this->drupalLogin($web_user); - - // Turn on the block render array display and make sure it shows up. - $edit = array( - 'render_example_show_block' => TRUE, - ); - $this->drupalPost('examples/render_example/altering', $edit, t('Save configuration')); - - $xpath_array = array( - "//div[@id='sidebar-first']//fieldset[starts-with(@id, 'edit-render-example-block-fieldset')]", - '//*[@id="content"]//fieldset[contains(@id,"edit-render-example-block-fieldset")]', - ); - $this->assertRenderResults($xpath_array); - - - // Turn off block render array display and turn on the page render array - // display. - $edit = array( - 'render_example_show_page' => TRUE, - 'render_example_show_block' => FALSE, - ); - $this->drupalPost('examples/render_example/altering', $edit, t('Save configuration')); - - $xpath_array = array( - '//*[@id="content"]//fieldset[starts-with(@id,"edit-render-example-page-fieldset")]', - ); - $this->assertRenderResults($xpath_array); - - // Add note about render arrays to the top of sidebar_first. - $edit = array( - 'render_example_note_about_render_arrays' => TRUE, - ); - $this->drupalPost('examples/render_example/altering', $edit, t('Save configuration')); - $xpath_array = array( - '//*[@id="sidebar-first"]//ol//li[starts-with(.,"Render arrays are everywhere")]', - ); - $this->assertRenderResults($xpath_array); - - // Move the navigation menu to the top of the content area. - $edit = array( - 'render_example_move_navigation_menu' => TRUE, - ); - $this->drupalPost('examples/render_example/altering', $edit, t('Save configuration')); - $xpath_array = array( - '//*[@id="content"]//h2[starts-with(.,"Navigation")]', - ); - $this->assertRenderResults($xpath_array); - - // Skip a test for reversing order of sidebar_first as I think it would - // be too fragile. - - // Test the addition of #prefix and #suffix - $edit = array( - 'render_example_prefix' => TRUE, - ); - $this->drupalPost('examples/render_example/altering', $edit, t('Save configuration')); - $xpath_array = array( - '//*[@id="sidebar-first"]//*[contains(@class, "block-prefix")]/span[contains(@class, "block-suffix")]', - ); - $this->assertRenderResults($xpath_array); - - // Test some rendering facets of the various render examples - $this->drupalGet('examples/render_example/arrays'); - $content = $this->xpath('//*[@class="render-array"][1]'); - - $xpath_array = array( - '//div[@class="rendered"][starts-with(.,"Some basic text in a #markup")]' => 'Some basic text in a #markup (shows basic markup and how it is rendered)', - '//div[@class="rendered"][starts-with(.,"This is some text that should be put to")]' => 'This is some text that should be put together | This is some more text that we need | ', - '//div[@class="rendered"][starts-with(.,"The current time was")]' => 'The current time was when this was cached. Updated every seconds', - '//div[@class="rendered"]/div[text()][starts-with(.,"(prefix)This one")]' => '(prefix)This one adds a prefix and suffix, which put a div around the item(suffix)', - '//div[@class="rendered"]/div[text()][starts-with(.,"markup for pre_")]' => 'markup for pre_render and post_render example', - '//div[@class="rendered"]/div[text()][starts-with(.,"This markup was added")]' => 'This markup was added after rendering by a #post_render', - '//div[@class="rendered"]/div[text()][starts-with(.,"This #suffix")]' => 'This #suffix was added by a #pre_render', - ); - $this->assertRenderedText($xpath_array); - } -} diff --git a/render_example/render_example.css b/render_example/render_example.css deleted file mode 100644 index 0da7f30..0000000 --- a/render_example/render_example.css +++ /dev/null @@ -1,20 +0,0 @@ -.render-array { - border: 2px solid #000; - margin-top: 10px; - padding-left: 5px; - padding-top: 5px; -} - -.render-header { - font-size: large; - font-style: italic; -} - -.unrendered-label { - font-style: italic; - margin-top: 10px; -} - -.rendered { - background-color: #add8e6; -} diff --git a/render_example/render_example.info b/render_example/render_example.info deleted file mode 100644 index e1b4605..0000000 --- a/render_example/render_example.info +++ /dev/null @@ -1,7 +0,0 @@ -name = Render example -description = Demonstrates drupal_render's capabilities and altering render arrays -package = Example modules -core = 8.x -; @todo Remove dependency? -dependencies[] = devel -stylesheets[all][] = render_example.css diff --git a/render_example/render_example.info.yml b/render_example/render_example.info.yml new file mode 100644 index 0000000..0788ec1 --- /dev/null +++ b/render_example/render_example.info.yml @@ -0,0 +1,12 @@ +name: Render example +description: "Demonstrates drupal_render's capabilities and altering render arrays." +type: module +package: Example modules +core: 8.x +# @todo Remove dependency? +dependencies: + - devel + - examples +stylesheets: + all: + - css/render_example.css \ No newline at end of file diff --git a/render_example/render_example.install b/render_example/render_example.install deleted file mode 100644 index a6bd4cc..0000000 --- a/render_example/render_example.install +++ /dev/null @@ -1,16 +0,0 @@ - 'Render Example', - 'page callback' => 'render_example_info', - 'access callback' => TRUE, - ); - $items['examples/render_example/altering'] = array( - 'title' => 'Alter pages and blocks', - 'page callback' => 'drupal_get_form', - 'page arguments' => array('render_example_demo_form'), - 'access arguments' => array('access devel information'), - ); - $items['examples/render_example/arrays'] = array( - 'title' => 'Render array examples', - 'page callback' => 'render_example_arrays', - 'access callback' => TRUE, - ); - - return $items; -} - - -/** - * Simple basic information about the module; an entry point. - */ -function render_example_info() { - return t('The render example provides a ', array('!arrays' => url('examples/render_example/arrays'), '!alter' => url('examples/render_example/altering'))); -} - - -/** - * Provides a number of render arrays and show what they do. - * - * Each array is keyed by a description; it's returned for rendering at page - * render time. It's easy to add new examples to this. - * - * The array items in $demos are intended to be raw, normal render arrays - * that can be experimented with to end up with different outcomes. - */ -function render_example_arrays() { - - $interval = 60; // Interval in seconds for cache update with #cache. - - $demos = array( - // Demonstrate the simplest markup, a #markup element. - t('Super simple #markup') => array( - '#markup' => t('Some basic text in a #markup (shows basic markup and how it is rendered)'), - ), - - // Shows how #prefix and #suffix can add markup into an array. - t('Using #prefix and #suffix') => array( - '#markup' => t('This one adds a prefix and suffix, which put a div around the item'), - '#prefix' => '

(prefix)
', - '#suffix' => '
(suffix)
', - ), - - // When #theme is provided, it is the #theme function's job to figure out - // the meaning of the render array. The #theme function receives the entire - // element in $variables and must return it, where it will be the content - // of '#children'. When a #theme or other function is provided, custom - // properties can be invented and used as needed, as the #separator - // property provided here. - // - // If #theme is not provided, either explicitly or by the underlying - // element, then the children are rendered using their own properties and - // the results go into #children. - t('theme for an element') => array( - 'child' => array( - t('This is some text that should be put together'), - t('This is some more text that we need'), - ), - '#separator' => ' | ', // Made up for this theme function. - '#theme' => 'render_example_aggregate', - ), - - // #theme_wrappers provides an array of theme functions which theme the - // envelope or "wrapper" of a set of child elements. The theme function - // finds its element children (the sub-arrays) already rendered in - // '#children'. - t('theme_wrappers demonstration') => array( - 'child1' => array('#markup' => t('Markup for child1')), - 'child2' => array('#markup' => t('Markup for child2')), - '#theme_wrappers' => array('render_example_add_div', 'render_example_add_notes'), - ), - - // Add '#pre_render' and '#post_render' handlers. - // - '#pre_render' functions get access to the array before it is rendered - // and can change it. This is similar to a theme function, but it is a - // specific fixed function and changes the array in place rather than - // rendering it.. - // - '#post_render' functions get access to the rendered content, but also - // have the original array available. - t('pre_render and post_render') => array( - '#markup' => '
' . t('markup for pre_render and post_render example') . '
', - '#pre_render' => array('render_example_add_suffix'), - '#post_render' => array('render_example_add_prefix'), - ), - - // Cache an element for $interval seconds using #cache. - // The assumption here is that this is an expensive item to render, perhaps - // large or otherwise expensive. Of course here it's just a piece of markup, - // so we don't get the value. - // - // #cache allows us to set - // - 'keys', an array of strings that will create the string cache key. - // - 'bin', the cache bin - // - 'expire', the expire timestamp. Note that this is actually limited - // to the granularity of a cron run. - // - 'granularity', a bitmask determining at what level the caching is done - // (user, role, page). - t('cache demonstration') => array( - // If your expensive function were to be executed here it would happen - // on every page load regardless of the cache. The actual markup is - // added via the #pre_render function, so that drupal_render() will only - // execute the expensive function if this array has not been cached. - '#markup' => '', - '#pre_render' => array('render_example_cache_pre_render'), - '#cache' => array( - 'keys' => array('render_example', 'cache', 'demonstration'), - 'bin' => 'cache', - 'expire' => time() + $interval, - 'granularity' => DRUPAL_CACHE_PER_PAGE | DRUPAL_CACHE_PER_ROLE, - ), - ), - ); - - - // The rest of this function just places the above arrays in a context where - // they can be rendered (hopefully attractively and usefully) on the page. - $page_array = array(); - foreach ($demos as $key => $item) { - $page_array[$key]['#theme_wrappers'] = array('render_array'); - $page_array[$key]['#description'] = $key; - - $page_array[$key]['unrendered'] = array( - '#prefix' => '
' . t('Unrendered array (as plain text and with a krumo version)') . ':
', - '#type' => 'markup', - '#markup' => htmlentities(drupal_var_export($item)), - ); - $page_array[$key]['kpr'] = array( - // The kpr() function is from devel module and is here only allow us - // to output the array in a way that's easy to explore. - '#markup' => kpr($item, TRUE), - ); - $page_array[$key]['hr'] = array('#markup' => '
'); - $page_array[$key]['rendered'] = array($item); - $page_array[$key]['rendered']['#prefix'] = '

Rendered version (light blue):

' . '
'; - $page_array[$key]['rendered']['#suffix'] = '
'; - } - - return $page_array; -} +use Drupal\Core\Element; +use Drupal\Core\Config; /** * A '#pre_render' function. @@ -202,7 +46,10 @@ function render_example_cache_pre_render($element) { */ function render_example_cache_expensive() { $interval = 60; - $time_message = t('The current time was %time when this was cached. Updated every %interval seconds', array('%time' => date('r'), '%interval' => $interval)); + $time_message = t('The current time was %time when this was cached. Updated every %interval seconds', array( + '%time' => date('r'), + '%interval' => $interval + )); // Uncomment the following line to demonstrate that this function is not // being run when the rendered array is cached. // drupal_set_message($time_message); @@ -245,81 +92,12 @@ function render_example_add_prefix($markup, $element) { return $markup; } -/** - * A #theme function. - * - * This #theme function has the responsibility of consolidating/rendering the - * children's markup and returning it, where it will be placed in the - * element's #children property. - */ -function theme_render_example_aggregate($variables) { - $output = ''; - foreach (element_children($variables['element']['child']) as $item) { - $output .= $variables['element']['child'][$item] . $variables['element']['#separator']; - } - return $output; -} - /*************** Altering Section ************************** * The following section of the example builds and arranges the altering * example. */ /** - * Builds the form that offers options of what items to show. - */ -function render_example_demo_form($form, &$form_state) { - $form['description'] = array( - '#type' => 'markup', - '#markup' => t('This example shows what render arrays look like in the building of a page. It will not work unless the user running it has the "access devel information" privilege. It shows both the actual arrays used to build a page or block and also the capabilities of altering the page late in its lifecycle.'), - ); - - $form['show_arrays'] = array( - '#type' => 'fieldset', - '#title' => t('Show render arrays'), - ); - - foreach (array('block', 'page') as $type) { - $form['show_arrays']['render_example_show_' . $type] = array( - '#type' => 'checkbox', - '#title' => t('Show @type render arrays', array('@type' => $type)), - '#default_value' => variable_get('render_example_show_' . $type, FALSE), - ); - } - - $form['page_fiddling'] = array( - '#type' => 'fieldset', - '#title' => t('Make changes on page via hook_page_alter()'), - ); - $form['page_fiddling']['render_example_note_about_render_arrays'] = array( - '#title' => t('Add a note about render arrays to top of sidebar_first (if it exists)'), - '#description' => t('Creates a simple render array that displays the use of #pre_render, #post_render, #theme, and #theme_wrappers.'), - '#type' => 'checkbox', - '#default_value' => variable_get('render_example_note_about_render_arrays', FALSE), - ); - $form['page_fiddling']['render_example_move_navigation_menu'] = array( - '#title' => t('Move the navigation menu to the top of the content area'), - '#description' => t('Uses hook_page_alter() to move the navigation menu into another region.'), - '#type' => 'checkbox', - '#default_value' => variable_get('render_example_move_navigation_menu', FALSE), - ); - $form['page_fiddling']['render_example_reverse_sidebar'] = array( - '#title' => t('Reverse ordering of sidebar_first elements (if it exists) - will affect the above'), - '#description' => t('Uses hook_page_alter() to reverse the ordering of items in sidebar_first'), - '#type' => 'checkbox', - '#default_value' => variable_get('render_example_reverse_sidebar', FALSE), - ); - $form['page_fiddling']['render_example_prefix'] = array( - '#title' => t('Use #prefix and #suffix to wrap a div around every block'), - '#description' => t('Uses hook_page_alter to wrap all blocks with a div using #prefix and #suffix'), - '#type' => 'checkbox', - '#default_value' => variable_get('render_example_prefix'), - ); - - return system_settings_form($form); -} - -/** * Implements hook_page_alter(). * * Alters the page in several different ways based on how the form has been @@ -328,11 +106,12 @@ function render_example_demo_form($form, &$form_state) { function render_example_page_alter(&$page) { $original_page = $page; + $config = \Drupal::config('render_example.settings'); // Re-sort the sidebar in reverse order. - if (variable_get('render_example_reverse_sidebar', FALSE) && !empty($page['sidebar_first'])) { + if ($config->get('render_example_reverse_sidebar') && !empty($page['sidebar_first'])) { $page['sidebar_first'] = array_reverse($page['sidebar_first']); - foreach (element_children($page['sidebar_first']) as $element) { + foreach (Element::children($page['sidebar_first']) as $element) { // Reverse the weights if they exist. if (!empty($page['sidebar_first'][$element]['#weight'])) { $page['sidebar_first'][$element]['#weight'] *= -1; @@ -343,7 +122,7 @@ function render_example_page_alter(&$page) { // Add a list of items to the top of sidebar_first. // This shows how #theme and #theme_wrappers work. - if (variable_get('render_example_note_about_render_arrays', FALSE) && !empty($page['sidebar_first'])) { + if ($config->get('render_example_note_about_render_arrays') && !empty($page['sidebar_first'])) { $items = array( t('Render arrays are everywhere in D7'), t('Leave content unrendered as much as possible'), @@ -353,7 +132,6 @@ function render_example_page_alter(&$page) { $note = array( '#title' => t('Render Array Example'), '#items' => $items, - // The functions in #pre_render get to alter the actual data before it // gets rendered by the various theme functions. '#pre_render' => array('render_example_change_to_ol'), @@ -365,16 +143,18 @@ function render_example_page_alter(&$page) { '#theme' => 'item_list', // Then the theme operations in #theme_wrappers can wrap more around // what #theme left in #chilren. - '#theme_wrappers' => array('render_example_add_div', 'render_example_add_notes'), + '#theme_wrappers' => array( + 'render_example_add_div', + 'render_example_add_notes' + ), '#weight' => -9999, ); $page['sidebar_first']['render_array_note'] = $note; $page['sidebar_first']['#sorted'] = FALSE; } - // Move the navigation menu into the content area. - if (variable_get('render_example_move_navigation_menu', FALSE) && !empty($page['sidebar_first']['system_navigation']) && !empty($page['content'])) { + if ($config->get('render_example_move_navigation_menu') && !empty($page['sidebar_first']['system_navigation']) && !empty($page['content'])) { $page['content']['system_navigation'] = $page['sidebar_first']['system_navigation']; $page['content']['system_navigation']['#weight'] = -99999; unset($page['content']['#sorted']); @@ -382,7 +162,7 @@ function render_example_page_alter(&$page) { } // Show the render array used to build the page render array display. - if (variable_get('render_example_show_page', FALSE)) { + if ($config->get('render_example_show_page')) { $form['render_example_page_fieldset'] = array( '#type' => 'fieldset', '#title' => t('Page render array'), @@ -400,9 +180,9 @@ function render_example_page_alter(&$page) { } // Add render array to the bottom of each block - if (variable_get('render_example_show_block', FALSE)) { - foreach (element_children($page) as $region_name) { - foreach (element_children($page[$region_name]) as $block_name) { + if ($config->get('render_example.show_block')) { + foreach (Element::children($page) as $region_name) { + foreach (Element::children($page[$region_name]) as $block_name) { // Push the block down a level so we can add another block after it. $old_block = $page[$region_name][$block_name]; @@ -425,16 +205,16 @@ function render_example_page_alter(&$page) { ); // Add the new block that contains the render array. - $page[$region_name][$block_name]['render_example_block_render_array'] = drupal_get_form('render_example_embedded_form', $form); + $page[$region_name][$block_name]['render_example_block_render_array'] = drupal_get_form('render_example_embedded_form', $form); $page[$region_name][$block_name]['render_example_block_render_array']['#weight'] = 999; } } } // Add #prefix and #suffix to a block to wrap a div around it. - if (variable_get('render_example_prefix', FALSE)) { - foreach (element_children($page) as $region_name) { - foreach (element_children($page[$region_name]) as $block_name) { + if ($config->get('render_example_prefix')) { + foreach (Element::children($page) as $region_name) { + foreach (Element::children($page[$region_name]) as $block_name) { $block = &$page[$region_name][$block_name]; $block['#prefix'] = '

Prefixed

'; $block['#suffix'] = 'Block suffix
'; @@ -480,41 +260,8 @@ function render_example_theme() { 'render element' => 'element', ), ); - return $items; -} - -/** - * Wraps a div around the already-rendered #children. - */ -function theme_render_example_add_div($variables) { - $element = $variables['element']; - $output = '
'; - $output .= $element['#children']; - $output .= '
'; - return $output; -} - -/** - * Wraps a div and add a little text after the rendered #children. - */ -function theme_render_example_add_notes($variables) { - $element = $variables['element']; - $output = '
'; - $output .= $element['#children']; - $output .= '' . t('This is a note added by a #theme_wrapper') . ''; - $output .= '
'; - return $output; -} - -/** - * Themes the render array (from the demonstration page). - */ -function theme_render_array($variables) { - $heading = '
' . $variables['element']['#description'] . '
'; - - $rendered = '
' . $heading . $variables['element']['#children'] . '
'; - return $rendered; + return $items; } /** diff --git a/render_example/render_example.routing.yml b/render_example/render_example.routing.yml new file mode 100644 index 0000000..16e9436 --- /dev/null +++ b/render_example/render_example.routing.yml @@ -0,0 +1,21 @@ +render_example.description: + path: 'examples/render_example' + defaults: + _controller: '\Drupal\render_example\Controller\RenderExampleController::description' + requirements: + _access: 'TRUE' + +render_example.altering: + path: 'examples/render_example/altering' + defaults: + _form: '\Drupal\render_example\Form\RenderExampleDemoForm' + _title: 'Alter pages and blocks' + requirements: + _access: 'TRUE' + +render_example.arrays: + path: 'examples/render_example/arrays' + defaults: + _controller: '\Drupal\render_example\Controller\RenderExampleController::arrays' + requirements: + _access: 'TRUE' \ No newline at end of file diff --git a/render_example/src/Controller/RenderExampleController.php b/render_example/src/Controller/RenderExampleController.php new file mode 100644 index 0000000..013b866 --- /dev/null +++ b/render_example/src/Controller/RenderExampleController.php @@ -0,0 +1,137 @@ + t('The render example provides a ', array( + '!arrays' => \Drupal::url('render_example.arrays'), + '!alter' => \Drupal::url('render_example.altering') + )), + ); + + return $output; + } + + public function arrays() { + $interval = 60; // Interval in seconds for cache update with #cache. + + $demos = array( + // Demonstrate the simplest markup, a #markup element. + t('Super simple #markup') => array( + '#markup' => t('Some basic text in a #markup (shows basic markup and how it is rendered)'), + ), + // Shows how #prefix and #suffix can add markup into an array. + t('Using #prefix and #suffix') => array( + '#markup' => t('This one adds a prefix and suffix, which put a div around the item'), + '#prefix' => '

(prefix)
', + '#suffix' => '
(suffix)
', + ), + // When #theme is provided, it is the #theme function's job to figure out + // the meaning of the render array. The #theme function receives the entire + // element in $variables and must return it, where it will be the content + // of '#children'. When a #theme or other function is provided, custom + // properties can be invented and used as needed, as the #separator + // property provided here. + // + // If #theme is not provided, either explicitly or by the underlying + // element, then the children are rendered using their own properties and + // the results go into #children. + t('theme for an element') => array( + 'child' => array( + t('This is some text that should be put together'), + t('This is some more text that we need'), + ), + '#separator' => ' | ', // Made up for this theme function. + '#theme' => 'render_example_aggregate', + ), + // #theme_wrappers provides an array of theme functions which theme the + // envelope or "wrapper" of a set of child elements. The theme function + // finds its element children (the sub-arrays) already rendered in + // '#children'. + t('theme_wrappers demonstration') => array( + 'child1' => array('#markup' => t('Markup for child1')), + 'child2' => array('#markup' => t('Markup for child2')), + '#theme_wrappers' => array( + 'render_example_add_div', + 'render_example_add_notes' + ), + ), + // Add '#pre_render' and '#post_render' handlers. + // - '#pre_render' functions get access to the array before it is rendered + // and can change it. This is similar to a theme function, but it is a + // specific fixed function and changes the array in place rather than + // rendering it.. + // - '#post_render' functions get access to the rendered content, but also + // have the original array available. + t('pre_render and post_render') => array( + '#markup' => '
' . t('markup for pre_render and post_render example') . '
', + '#pre_render' => array('render_example_add_suffix'), + '#post_render' => array('render_example_add_prefix'), + ), + // Cache an element for $interval seconds using #cache. + // The assumption here is that this is an expensive item to render, perhaps + // large or otherwise expensive. Of course here it's just a piece of markup, + // so we don't get the value. + // + // #cache allows us to set + // - 'keys', an array of strings that will create the string cache key. + // - 'bin', the cache bin + // - 'expire', the expire timestamp. Note that this is actually limited + // to the granularity of a cron run. + // - 'granularity', a bitmask determining at what level the caching is done + // (user, role, page). + t('cache demonstration') => array( + // If your expensive function were to be executed here it would happen + // on every page load regardless of the cache. The actual markup is + // added via the #pre_render function, so that drupal_render() will only + // execute the expensive function if this array has not been cached. + '#markup' => '', + '#pre_render' => array('render_example_cache_pre_render'), + '#cache' => array( + 'keys' => array('render_example', 'cache', 'demonstration'), + 'bin' => 'cache', + 'expire' => time() + $interval, + 'granularity' => DRUPAL_CACHE_PER_PAGE | DRUPAL_CACHE_PER_ROLE, + ), + ), + ); + + + // The rest of this function just places the above arrays in a context where + // they can be rendered (hopefully attractively and usefully) on the page. + $page_array = array(); + foreach ($demos as $key => $item) { + $page_array[$key]['#theme_wrappers'] = array('render_array'); + $page_array[$key]['#description'] = $key; + + $page_array[$key]['unrendered'] = array( + '#prefix' => '
' . t('Unrendered array (as plain text and with a krumo version)') . ':
', + '#type' => 'markup', + '#markup' => htmlentities(Variable::export($item)), + ); + $page_array[$key]['kpr'] = array( + // The kpr() function is from devel module and is here only allow us + // to output the array in a way that's easy to explore. + '#markup' => kpr($item, TRUE), + ); + $page_array[$key]['hr'] = array('#markup' => '
'); + $page_array[$key]['rendered'] = array($item); + $page_array[$key]['rendered']['#prefix'] = '

Rendered version (light blue):

' . '
'; + $page_array[$key]['rendered']['#suffix'] = '
'; + } + + return $page_array; + } + +} \ No newline at end of file diff --git a/render_example/src/Form/RenderExampleDemoForm.php b/render_example/src/Form/RenderExampleDemoForm.php new file mode 100644 index 0000000..6f500d9 --- /dev/null +++ b/render_example/src/Form/RenderExampleDemoForm.php @@ -0,0 +1,120 @@ +config('render_example.settings'); + + $form['description'] = array( + '#type' => 'markup', + '#markup' => t('This example shows what render arrays look like in the building of a page. It will not work unless the user running it has the "access devel information" privilege. It shows both the actual arrays used to build a page or block and also the capabilities of altering the page late in its lifecycle.'), + ); + + $form['show_arrays'] = array( + '#type' => 'fieldset', + '#title' => t('Show render arrays'), + ); + + foreach ($this->show_arrays as $type) { + $form['show_arrays']['render_example_show_' . $type] = array( + '#type' => 'checkbox', + '#title' => t('Show @type render arrays', array('@type' => $type)), + '#default_value' => $config->get("render_example.show_{$type}"), + ); + } + + $form['page_fiddling'] = array( + '#type' => 'fieldset', + '#title' => t('Make changes on page via hook_page_alter()'), + ); + $form['page_fiddling']['render_example_note_about_render_arrays'] = array( + '#title' => t('Add a note about render arrays to top of sidebar_first (if it exists)'), + '#description' => t('Creates a simple render array that displays the use of #pre_render, #post_render, #theme, and #theme_wrappers.'), + '#type' => 'checkbox', + '#default_value' => $config->get('render_example.note_about_render_arrays'), + ); + $form['page_fiddling']['render_example_move_navigation_menu'] = array( + '#title' => t('Move the navigation menu to the top of the content area'), + '#description' => t('Uses hook_page_alter() to move the navigation menu into another region.'), + '#type' => 'checkbox', + '#default_value' => $config->get('render_example.move_navigation_menu'), + ); + $form['page_fiddling']['render_example_reverse_sidebar'] = array( + '#title' => t('Reverse ordering of sidebar_first elements (if it exists) - will affect the above'), + '#description' => t('Uses hook_page_alter() to reverse the ordering of items in sidebar_first'), + '#type' => 'checkbox', + '#default_value' => $config->get('render_example.reverse_sidebar'), + ); + $form['page_fiddling']['render_example_prefix'] = array( + '#title' => t('Use #prefix and #suffix to wrap a div around every block'), + '#description' => t('Uses hook_page_alter to wrap all blocks with a div using #prefix and #suffix'), + '#type' => 'checkbox', + '#default_value' => $config->get('render_example.prefix'), + ); + + return parent::buildForm($form, $form_state); + } + + /** + * {@inheritdoc} + */ + protected function getEditableConfigNames() { + return ['render_example.settings']; + } + + /** + * {@inheritdoc} + */ + public function submitForm(array &$form, FormStateInterface $form_state) { + $values = $form_state->getValues(); + + $config = $this->config('render_example.settings'); + foreach ($this->show_arrays as $type) { + $config->set("render_example.show_{$type}", $values["render_example_show_{$type}"]) + ->save(); + } + $config->set('render_example.note_about_render_arrays', $values['render_example_note_about_render_arrays']) + ->save(); + $config->set('render_example.move_navigation_menu', $values['render_example_move_navigation_menu']) + ->save(); + $config->set('render_example.reverse_sidebar', $values['render_example_reverse_sidebar']) + ->save(); + $config->set('render_example.prefix', $values['render_example_prefix']) + ->save(); + + parent::submitForm($form, $form_state); + } + +} \ No newline at end of file diff --git a/render_example/src/Tests/RenderExampleTest.php b/render_example/src/Tests/RenderExampleTest.php new file mode 100644 index 0000000..35d3bd2 --- /dev/null +++ b/render_example/src/Tests/RenderExampleTest.php @@ -0,0 +1,140 @@ +xpath($xpath); + $this->assertTrue(!empty($result), format_string('Found xpath %xpath', array('%xpath' => $xpath))); + } + } + + /** + * Asserts that the string value of the result is the same as the passed text. + * + * @param $xpath_array + * Array of keyed arrays of tests to be made. Each child array consists of + * $xpath => $expected_text + */ + protected function assertRenderedText($xpath_array) { + foreach ($xpath_array as $xpath => $text) { + $result = $this->xpath($xpath); + $this->assertTrue((string) $result[0][0] == $text, format_string('%ary selects text %text', array( + '%ary' => $xpath, + '%text' => $text + ))); + } + } + + /** + * Login user, create an example node, and test blog functionality through the admin and user interfaces. + */ + public function testRenderExampleBasic() { + // Create a user that can access devel information and log in. + $web_user = $this->drupalCreateUser(array( + 'access devel information', + 'access content' + )); + $this->drupalLogin($web_user); + + // Turn on the block render array display and make sure it shows up. + $edit = array( + 'render_example_show_block' => TRUE, + ); + $this->drupalPost('examples/render_example/altering', $edit, t('Save configuration')); + + $xpath_array = array( + "//div[@id='sidebar-first']//fieldset[starts-with(@id, 'edit-render-example-block-fieldset')]", + '//*[@id="content"]//fieldset[contains(@id,"edit-render-example-block-fieldset")]', + ); + $this->assertRenderResults($xpath_array); + + + // Turn off block render array display and turn on the page render array + // display. + $edit = array( + 'render_example_show_page' => TRUE, + 'render_example_show_block' => FALSE, + ); + $this->drupalPost('examples/render_example/altering', $edit, t('Save configuration')); + + $xpath_array = array( + '//*[@id="content"]//fieldset[starts-with(@id,"edit-render-example-page-fieldset")]', + ); + $this->assertRenderResults($xpath_array); + + // Add note about render arrays to the top of sidebar_first. + $edit = array( + 'render_example_note_about_render_arrays' => TRUE, + ); + $this->drupalPost('examples/render_example/altering', $edit, t('Save configuration')); + $xpath_array = array( + '//*[@id="sidebar-first"]//ol//li[starts-with(.,"Render arrays are everywhere")]', + ); + $this->assertRenderResults($xpath_array); + + // Move the navigation menu to the top of the content area. + $edit = array( + 'render_example_move_navigation_menu' => TRUE, + ); + $this->drupalPost('examples/render_example/altering', $edit, t('Save configuration')); + $xpath_array = array( + '//*[@id="content"]//h2[starts-with(.,"Navigation")]', + ); + $this->assertRenderResults($xpath_array); + + // Skip a test for reversing order of sidebar_first as I think it would + // be too fragile. + + // Test the addition of #prefix and #suffix + $edit = array( + 'render_example_prefix' => TRUE, + ); + $this->drupalPost('examples/render_example/altering', $edit, t('Save configuration')); + $xpath_array = array( + '//*[@id="sidebar-first"]//*[contains(@class, "block-prefix")]/span[contains(@class, "block-suffix")]', + ); + $this->assertRenderResults($xpath_array); + + // Test some rendering facets of the various render examples + $this->drupalGet('examples/render_example/arrays'); + $content = $this->xpath('//*[@class="render-array"][1]'); + + $xpath_array = array( + '//div[@class="rendered"][starts-with(.,"Some basic text in a #markup")]' => 'Some basic text in a #markup (shows basic markup and how it is rendered)', + '//div[@class="rendered"][starts-with(.,"This is some text that should be put to")]' => 'This is some text that should be put together | This is some more text that we need | ', + '//div[@class="rendered"][starts-with(.,"The current time was")]' => 'The current time was when this was cached. Updated every seconds', + '//div[@class="rendered"]/div[text()][starts-with(.,"(prefix)This one")]' => '(prefix)This one adds a prefix and suffix, which put a div around the item(suffix)', + '//div[@class="rendered"]/div[text()][starts-with(.,"markup for pre_")]' => 'markup for pre_render and post_render example', + '//div[@class="rendered"]/div[text()][starts-with(.,"This markup was added")]' => 'This markup was added after rendering by a #post_render', + '//div[@class="rendered"]/div[text()][starts-with(.,"This #suffix")]' => 'This #suffix was added by a #pre_render', + ); + $this->assertRenderedText($xpath_array); + } + +} \ No newline at end of file diff --git a/render_example/templates/render-array.html.twig b/render_example/templates/render-array.html.twig new file mode 100644 index 0000000..d2a8bed --- /dev/null +++ b/render_example/templates/render-array.html.twig @@ -0,0 +1,19 @@ +{# +/** + * @file + * Default theme implementation to render_array. + * + * Themes the render array (from the demonstration page). + * + * Available variables: + * - element: Element that will be rendered. + * + * @ingroup themeable + */ +#} +
+
+ {{ element['#description'] }} +
+ {{ element['#children'] }} +
\ No newline at end of file diff --git a/render_example/templates/render-example-add-div.html.twig b/render_example/templates/render-example-add-div.html.twig new file mode 100644 index 0000000..6a0e210 --- /dev/null +++ b/render_example/templates/render-example-add-div.html.twig @@ -0,0 +1,16 @@ +{# +/** + * @file + * Default theme implementation to render_example_add_div. + * + * Wraps a div around the already-rendered #children. + * + * Available variables: + * - element: Element that will be rendered. + * + * @ingroup themeable + */ +#} +
+ {{ element['#children'] }} +
\ No newline at end of file diff --git a/render_example/templates/render-example-add-notes.html.twig b/render_example/templates/render-example-add-notes.html.twig new file mode 100644 index 0000000..ab3676e --- /dev/null +++ b/render_example/templates/render-example-add-notes.html.twig @@ -0,0 +1,17 @@ +{# +/** + * @file + * Default theme implementation to render_example_add_notes. + * + * Wraps a div and add a little text after the rendered #children. + * + * Available variables: + * - element: Element that will be rendered. + * + * @ingroup themeable + */ +#} +
+ {{ element['#children'] }} + {% trans %}This is a note added by a #theme_wrapper{% endtrans %} +
\ No newline at end of file diff --git a/render_example/templates/render-example-aggregate.html.twig b/render_example/templates/render-example-aggregate.html.twig new file mode 100644 index 0000000..3b920fc --- /dev/null +++ b/render_example/templates/render-example-aggregate.html.twig @@ -0,0 +1,17 @@ +{# +/** + * @file + * Default theme implementation to render_example_aggregate. + * + * The contents are rendered above feed listings when browsing source feeds. + * For example, "example.com/aggregator/sources/1". + * + * Available variables: + * - element: Element that will be rendered. + * + * @ingroup themeable + */ +#} +{%- for item in element.child -%} + {{ item }} {{ element['#separator'] }} +{%- endfor -%} \ No newline at end of file