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