diff --git a/core/MAINTAINERS.txt b/core/MAINTAINERS.txt index 75d48e4..d479f08 100644 --- a/core/MAINTAINERS.txt +++ b/core/MAINTAINERS.txt @@ -242,6 +242,9 @@ Path module PHP module - ? +Picture module +- Peter Droogmans 'attiks' http://drupal.org/user/105002 + Poll module - Andrei Mateescu 'amateescu' http://drupal.org/user/729614 diff --git a/core/includes/common.inc b/core/includes/common.inc index 4c86953..540f42b 100644 --- a/core/includes/common.inc +++ b/core/includes/common.inc @@ -3759,15 +3759,15 @@ function drupal_region_class($region) { * else being the same, JavaScript added by a call to drupal_add_js() that * happened later in the page request gets added to the page after one for * which drupal_add_js() happened earlier in the page request. - * - defer: If set to TRUE, the defer attribute is set on the <script> - * tag. Defaults to FALSE. - * - async: If set to TRUE, the async attribute is set on the <script> - * tag. Defaults to FALSE. * - cache: If set to FALSE, the JavaScript file is loaded anew on every page * call; in other words, it is not cached. Used only when 'type' references * a JavaScript file. Defaults to TRUE. * - preprocess: If TRUE and JavaScript aggregation is enabled, the script * file will be aggregated. Defaults to TRUE. + * - attributes: An associative array of attributes for the '; - $expected_2 = ''; + $expected_1 = ''; + $expected_2 = ''; - $this->assertTrue(strpos($javascript, $expected_1) > 0, 'Rendered external JavaScript with correct async attribute.'); - $this->assertTrue(strpos($javascript, $expected_2) > 0, 'Rendered internal JavaScript with correct async attribute.'); + $this->assertTrue(strpos($javascript, $expected_1) > 0, 'Rendered external JavaScript with correct defer attribute.'); + $this->assertTrue(strpos($javascript, $expected_2) > 0, 'Rendered internal JavaScript with correct defer attribute.'); } /** - * Tests adding external JavaScript Files with the defer attribute. + * Tests that attributes are maintained when JS aggregation is enabled. */ - function testDeferAttribute() { + function testAggregatedAttributes() { + // Enable aggregation. + config('system.performance')->set('preprocess.js', 1)->save(); + $default_query_string = variable_get('css_js_query_string', '0'); drupal_add_library('system', 'drupal'); - drupal_add_js('http://example.com/script.js', array('defer' => TRUE)); - drupal_add_js('core/misc/collapse.js', array('defer' => TRUE)); + drupal_add_js('http://example.com/script.js', array('attributes' => array('defer' => 'defer'))); + drupal_add_js('core/misc/collapse.js', array('attributes' => array('defer' => 'defer'))); $javascript = drupal_get_js(); $expected_1 = ''; $expected_2 = ''; - $this->assertTrue(strpos($javascript, $expected_1) > 0, 'Rendered external JavaScript with correct defer attribute.'); - $this->assertTrue(strpos($javascript, $expected_2) > 0, 'Rendered internal JavaScript with correct defer attribute.'); + $this->assertTrue(strpos($javascript, $expected_1) > 0, 'Rendered external JavaScript with correct defer attribute with aggregation enabled.'); + $this->assertTrue(strpos($javascript, $expected_2) > 0, 'Rendered internal JavaScript with correct defer attribute with aggregation enabled.'); } /** diff --git a/core/modules/system/lib/Drupal/system/Tests/Routing/FirstEntryFinalMatcherTest.php b/core/modules/system/lib/Drupal/system/Tests/Routing/FirstEntryFinalMatcherTest.php index a288b9e..c44a492 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Routing/FirstEntryFinalMatcherTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Routing/FirstEntryFinalMatcherTest.php @@ -61,7 +61,7 @@ public function testFinalMatcherStatic() { $matcher->setCollection($collection); $attributes = $matcher->matchRequest($request); - $this->assertEqual($attributes['_route'], 'route_a', 'The correct matching route was found.'); + $this->assertEqual($attributes['_route']->getOption('_name'), 'route_a', 'The correct matching route was found.'); $this->assertEqual($attributes['_controller'], 'foo', 'The correct controller was found.'); } @@ -82,7 +82,7 @@ public function testFinalMatcherPattern() { $matcher->setCollection($collection); $attributes = $matcher->matchRequest($request); - $this->assertEqual($attributes['_route'], 'route_a', 'The correct matching route was found.'); + $this->assertEqual($attributes['_route']->getOption('_name'), 'route_a', 'The correct matching route was found.'); $this->assertEqual($attributes['_controller'], 'foo', 'The correct controller was found.'); $this->assertEqual($attributes['value'], 'narf', 'Required placeholder value found.'); } @@ -105,7 +105,7 @@ public function testFinalMatcherPatternDefalts() { $matcher->setCollection($collection); $attributes = $matcher->matchRequest($request); - $this->assertEqual($attributes['_route'], 'route_a', 'The correct matching route was found.'); + $this->assertEqual($attributes['_route']->getOption('_name'), 'route_a', 'The correct matching route was found.'); $this->assertEqual($attributes['_controller'], 'foo', 'The correct controller was found.'); $this->assertEqual($attributes['value'], 'poink', 'Optional placeholder value used default.'); } diff --git a/core/modules/system/lib/Drupal/system/Tests/Routing/HttpMethodMatcherTest.php b/core/modules/system/lib/Drupal/system/Tests/Routing/HttpMethodMatcherTest.php index c98da2e..8055743 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Routing/HttpMethodMatcherTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Routing/HttpMethodMatcherTest.php @@ -44,7 +44,7 @@ function __construct($test_id = NULL) { $this->fixtures = new RoutingFixtures(); } - + /** * Confirms that the HttpMethod matcher matches properly. */ @@ -78,7 +78,7 @@ public function testNestedMatcher() { $attributes = $matcher->matchRequest($request); - $this->assertEqual($attributes['_route'], 'route_a', 'The correct matching route was found.'); + $this->assertEqual($attributes['_route']->getOption('_name'), 'route_a', 'The correct matching route was found.'); } /** diff --git a/core/modules/system/lib/Drupal/system/Tests/Routing/NestedMatcherTest.php b/core/modules/system/lib/Drupal/system/Tests/Routing/NestedMatcherTest.php index 444785c..de29538 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Routing/NestedMatcherTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Routing/NestedMatcherTest.php @@ -60,6 +60,6 @@ public function testNestedMatcher() { $attributes = $matcher->matchRequest($request); - $this->assertEqual($attributes['_route'], 'route_a', 'The correct matching route was found.'); + $this->assertEqual($attributes['_route']->getOption('_name'), 'route_a', 'The correct matching route was found.'); } } diff --git a/core/modules/system/lib/Drupal/system/Tests/Routing/RouterPermissionTest.php b/core/modules/system/lib/Drupal/system/Tests/Routing/RouterPermissionTest.php new file mode 100644 index 0000000..3050e4f --- /dev/null +++ b/core/modules/system/lib/Drupal/system/Tests/Routing/RouterPermissionTest.php @@ -0,0 +1,56 @@ + 'Router Permission tests', + 'description' => 'Function Tests for the routing permission system.', + 'group' => 'Routing', + ); + } + + /** + * Confirms that the router can get to a controller. + */ + public function testPermissionAccessDenied() { + + $this->drupalGet('router_test/test5'); + $this->assertResponse(403, 'Access denied for a route where we don\'t have a permission'); + } + + /** + * Confirms that our default controller logic works properly. + */ + public function testPermissionAccessPassed() { + + $user = $this->drupalCreateUser(array('access test5')); + + $this->drupalGet('router_test/test5'); + $this->assertRaw('test5', 'The correct string was returned because the route was successful.'); + + } +} diff --git a/core/modules/system/lib/Drupal/system/Tests/Theme/ThemeTest.php b/core/modules/system/lib/Drupal/system/Tests/Theme/ThemeTest.php index bd6c93b..f1308c5 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Theme/ThemeTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Theme/ThemeTest.php @@ -187,4 +187,19 @@ function testRegistryRebuild() { function testClassLoading() { new ThemeClass(); } + + /** + * Tests drupal_find_theme_templates(). + */ + public function testFindThemeTemplates() { + $cache = array(); + + // Prime the theme cache. + foreach (module_implements('theme') as $module) { + _theme_process_registry($cache, $module, 'module', $module, drupal_get_path('module', $module)); + } + + $templates = drupal_find_theme_templates($cache, '.tpl.php', drupal_get_path('theme', 'test_theme')); + $this->assertEqual($templates['node__1']['template'], 'node--1', 'Template node--1.tpl.php was found in test_theme.'); + } } diff --git a/core/modules/system/lib/Drupal/system/Tests/Theme/ThemeTestTwig.php b/core/modules/system/lib/Drupal/system/Tests/Theme/ThemeTestTwig.php new file mode 100644 index 0000000..6a9d988 --- /dev/null +++ b/core/modules/system/lib/Drupal/system/Tests/Theme/ThemeTestTwig.php @@ -0,0 +1,70 @@ + 'Twig Engine', + 'description' => 'Test theme functions with twig.', + 'group' => 'Theme', + ); + } + + function setUp() { + parent::setUp(); + theme_enable(array('test_theme_twig')); + } + + /** + * Ensures a themes template is overrideable based on the 'template' filename. + */ + function testTemplateOverride() { + variable_set('theme_default', 'test_theme_twig'); + $this->drupalGet('theme-test/template-test'); + $this->assertText('Success: Template overridden.', t('Template overridden by defined \'template\' filename.')); + } + + /** + * Tests drupal_find_theme_templates + */ + function testFindThemeTemplates() { + + $cache = array(); + + // Prime the theme cache + foreach (module_implements('theme') as $module) { + _theme_process_registry($cache, $module, 'module', $module, drupal_get_path('module', $module)); + } + + // Check for correct content + // @todo Remove this tests once double engine code is removed + + $this->assertEqual($cache['node']['template_file'], 'core/modules/node/templates/node.twig', 'Node is using node.twig as template file'); + $this->assertEqual($cache['node']['engine'], 'twig', 'Node is using twig engine'); + + $this->assertEqual($cache['theme_test_template_test']['template_file'], 'core/modules/system/tests/modules/theme_test/templates/theme_test.template_test.tpl.php', 'theme_test is using theme_test.template_test.tpl.php as template file'); + $this->assertEqual($cache['theme_test_template_test']['engine'], 'phptemplate', 'theme_test is using phptemplate as engine.'); + + $templates = drupal_find_theme_templates($cache, '.twig', drupal_get_path('theme', 'test_theme_twig')); + $this->assertEqual($templates['node__1']['template'], 'node--1', 'Template node--1.twig was found in test_theme_twig.'); + } +} diff --git a/core/modules/system/lib/Drupal/system/Tests/Theme/TwigReferenceObjectTest.php b/core/modules/system/lib/Drupal/system/Tests/Theme/TwigReferenceObjectTest.php new file mode 100644 index 0000000..88a4d22 --- /dev/null +++ b/core/modules/system/lib/Drupal/system/Tests/Theme/TwigReferenceObjectTest.php @@ -0,0 +1,18 @@ +nid = $nid; + $this->title = $title; + } +} diff --git a/core/modules/system/lib/Drupal/system/Tests/Theme/TwigReferenceUnitTest.php b/core/modules/system/lib/Drupal/system/Tests/Theme/TwigReferenceUnitTest.php new file mode 100644 index 0000000..0f9534e --- /dev/null +++ b/core/modules/system/lib/Drupal/system/Tests/Theme/TwigReferenceUnitTest.php @@ -0,0 +1,147 @@ + 'Theme Twig References', + 'description' => 'Tests TwigReference functions', + 'group' => 'Theme', + ); + } + + function setUp() { + parent::setUp(); + $this->variables = array( + 'foo' => 'bar', + 'baz' => array( + 'foo' => '42', + 'bar' => '23', + ), + 'node' => new TwigReferenceObjectTest( + 42, + 'test node' + ) + ); + } + + /** + * Test function for TwigReference class + */ + function testTwigReference() { + // Create a new TwigReference wrapper + $wrapper = new TwigReference(); + $wrapper->setReference($this->variables); + + // Check that strings are returned as strings + $foo = $wrapper['foo']; + $this->assertEqual($foo, $this->variables['foo'], 'String returned from TwigReference is the same'); + $this->assertTrue(is_string($foo), 'String returned from TwigReference is of type string'); + + // Check that arrays are wrapped again as TwigReference objects + $baz = $wrapper['baz']; + $this->assertTrue(is_object($baz), 'Array returned from TwigReference is of type object'); + $this->assertTrue($baz instanceof TwigReference, 'Array returned from TwigReference is instance of TwigReference'); + + // Check that getReference is giving back a reference to the original array + + $ref = &$baz->getReference(); + $this->assertTrue(is_array($ref), 'getReference returns an array'); + + // Now modify $ref + $ref['#hidden'] = TRUE; + $this->assertEqual($ref['#hidden'], $this->variables['baz']['#hidden'], 'Property set on reference is passed to original array.'); + $this->assertEqual($ref['#hidden'], $baz['#hidden'], 'Property set on reference is passed to wrapper.'); + + // Now modify $baz + $baz['hi'] = 'hello'; + + $this->assertEqual($baz['hi'], $this->variables['baz']['hi'], 'Property set on TwigReference object is passed to original array.'); + $this->assertEqual($baz['hi'], $ref['hi'], 'Property set on TwigReference object is passed to reference.'); + + // Check that an object is passed through directly + $node = $wrapper['node']; + $this->assertTrue(is_object($node), 'Object returned from TwigReference is of type object'); + $this->assertTrue($node instanceof TwigReferenceObjectTest, 'Object returned from TwigReference is instance of TwigReferenceObjectTest'); + } + + /** + * Test function for TwigReferenceFunctions class + */ + function testTwigReferenceFunctions() { + + // Create wrapper + $content = &$this->variables; + + // Use twig nomenclature + $context['content'] = $content; + + // Twig converts {{ hide(content.baz) }} to the following code + + // This will have failed, because getAttribute returns a value and not a reference + + try { + if (isset($context["content"])) { + $_content_ = $context["content"]; + } + else { + $_content_ = NULL; + } + TwigReferenceFunctions::hide($this->getAttribute($_content_, "baz")); + } + catch (Exception $e) { + // Catch the critical warning that a value was passed by reference + } + $this->assertFalse(isset($content['baz']['#printed']), 'baz is not hidden in content after hide() via value'); + + // Now lets do the same with some TwigReference magic! + + $content_wrapper = new TwigReference(); + $content_wrapper->setReference($content); + $context['content'] = $content_wrapper; + + // Twig converts {{ hide(content.baz) }} to the following code + + // This will succeed, because getAttribute returns a value, but it is an object + + if (isset($context["content"])) { + $_content_ = $context["content"]; + } + else { + $_content_ = NULL; + } + TwigReferenceFunctions::hide($this->getAttribute($_content_, "baz")); + + $this->assertTrue(isset($content['baz']['#printed']), 'baz is hidden in content after hide() via TwigReference object'); + + $type = TwigReferenceFunctions::gettype($this->getAttribute($_content_, "baz")); + $this->assertEqual($type, 'array', 'Type returned via TwigReferenceFunctions:: is an array.'); + + $type = gettype($this->getAttribute($_content_, "baz")); + $this->assertEqual($type, 'object', 'Type returned without TwigReferenceFunctions:: is an object.'); + } + + /** + * Helper function to somehow simulate Twigs getAttribute function + */ + public function getAttribute($array, $offset) { + if (isset($array[$offset])) { + return $array[$offset]; + } + + return NULL; + } +} diff --git a/core/modules/system/system.api.php b/core/modules/system/system.api.php index 2d09534..0965579 100644 --- a/core/modules/system/system.api.php +++ b/core/modules/system/system.api.php @@ -566,51 +566,6 @@ function hook_menu_get_item_alter(&$router_item, $path, $original_map) { } /** - * Defines routes in the new router system. - * - * A route is a Symfony Route object. See the Symfony documentation for more - * details on the available options. Of specific note: - * - _controller: This is the PHP callable that will handle a request matching - * the route. - * - _content: This is the PHP callable that will handle the body of a request - * matching this route. A default controller will provide the page - * rendering around it. - * - * Typically you will only specify one or the other of those properties. - * - * @deprecated - * This mechanism for registering routes is temporary. It will be replaced - * by a more robust mechanism in the near future. It is documented here - * only for completeness. - */ -function hook_route_info() { - $collection = new RouteCollection(); - - $route = new Route('router_test/test1', array( - '_controller' => '\Drupal\router_test\TestControllers::test1' - )); - $collection->add('router_test_1', $route); - - $route = new Route('router_test/test2', array( - '_content' => '\Drupal\router_test\TestControllers::test2' - )); - $collection->add('router_test_2', $route); - - $route = new Route('router_test/test3/{value}', array( - '_content' => '\Drupal\router_test\TestControllers::test3' - )); - $collection->add('router_test_3', $route); - - $route = new Route('router_test/test4/{value}', array( - '_content' => '\Drupal\router_test\TestControllers::test4', - 'value' => 'narf', - )); - $collection->add('router_test_4', $route); - - return $collection; -} - -/** * Define menu items and page callbacks. * * This hook enables modules to register paths in order to define how URL diff --git a/core/modules/system/system.theme-rtl.css b/core/modules/system/system.theme-rtl.css index b13345a..eaf351b 100644 --- a/core/modules/system/system.theme-rtl.css +++ b/core/modules/system/system.theme-rtl.css @@ -67,18 +67,9 @@ ul.menu { /** * Markup generated by theme_menu_local_tasks(). */ -ul.primary { - padding: 0 1em 0 0; -} -ul.primary li a { - margin-right: 5px; - margin-left: 0.5em; -} -ul.secondary li { - border-left: 1px solid #ccc; - border-right: none; - display: inline; - padding: 0 1em; +.tabs > li { + margin-left: 0.3em; + margin-right: 0; } /** diff --git a/core/modules/system/system.theme.css b/core/modules/system/system.theme.css index 84c0078..61303d2 100644 --- a/core/modules/system/system.theme.css +++ b/core/modules/system/system.theme.css @@ -306,55 +306,29 @@ ul.inline li { /** * Markup generated by theme_menu_local_tasks(). */ -ul.primary { - border-bottom: 1px solid #bbb; - border-collapse: collapse; - height: auto; - line-height: normal; +div.tabs { + margin: 1em 0; +} +ul.tabs { list-style: none; - margin: 5px; - padding: 0 0 0 1em; /* LTR */ - white-space: nowrap; + margin: 0 0 0.5em; + padding: 0; } -ul.primary li { - display: inline; +.tabs > li { + display: inline-block; + margin-right: 0.3em; /* LTR */ } -ul.primary li a { - background-color: #ddd; - border-color: #bbb; - border-style: solid solid none solid; - border-width: 1px; - height: auto; - margin-right: 0.5em; /* LTR */ - padding: 0 1em; +.tabs a { + display: block; + padding: 0.2em 1em; text-decoration: none; } -ul.primary li.active a { - background-color: #fff; - border: 1px solid #bbb; - border-bottom: 1px solid #fff; -} -ul.primary li a:hover { +.tabs a.active { background-color: #eee; - border-color: #ccc; - border-bottom-color: #eee; -} -ul.secondary { - border-bottom: 1px solid #bbb; - padding: 0.5em 1em; - margin: 5px; -} -ul.secondary li { - border-right: 1px solid #ccc; /* LTR */ - display: inline; - padding: 0 1em; -} -ul.secondary a { - padding: 0; - text-decoration: none; } -ul.secondary a.active { - border-bottom: 4px solid #999; +.tabs a:focus, +.tabs a:hover { + background-color: #f5f5f5; } /** diff --git a/core/modules/system/templates/datetime.twig b/core/modules/system/templates/datetime.twig new file mode 100644 index 0000000..5ea386e --- /dev/null +++ b/core/modules/system/templates/datetime.twig @@ -0,0 +1,28 @@ +{# +/** + * Returns HTML for a date / time. + * + * Available variables + * - timestamp: (optional) A UNIX timestamp for the datetime attribute. If the + * datetime cannot be represented as a UNIX timestamp, use a valid datetime + * attribute value in attributes.datetime. + * - text: (optional) The content to display within the