diff --git a/core/CHANGELOG.txt b/core/CHANGELOG.txt index e87e931bc79b37f8a0d356a2076019ff6f8754fb..27c28f8a72ed41bff63a3e5e4e88e95b0bafe97e 100644 --- a/core/CHANGELOG.txt +++ b/core/CHANGELOG.txt @@ -1,6 +1,9 @@ Drupal 8.0, xxxx-xx-xx (development version) ---------------------- +- Improved entity system. + * Added support for saving and deleting entities through the controller. + * Entities are now classed objects, implementing EntityInterface. - Replaced the core routing system with one built on the Symfony2 framework. - Configuration: * Added a centralized file-based configuration system. diff --git a/core/COPYRIGHT.txt b/core/COPYRIGHT.txt index 07d68a700bd0f9aaebdd17f77ffb865351f37452..92c78581ca943b55d8e7711f974aed92ac2ad3a0 100644 --- a/core/COPYRIGHT.txt +++ b/core/COPYRIGHT.txt @@ -1,5 +1,4 @@ - -All Drupal code is Copyright 2001 - 2010 by the original authors. +All Drupal code is Copyright 2001 - 2012 by the original authors. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -21,8 +20,45 @@ Drupal includes works under other copyright notices and distributed according to the terms of the GNU General Public License or a compatible license, including: - jQuery - Copyright (c) 2008 - 2009 John Resig +Javascript + + Farbtastic - Copyright (c) 2010 Matt Farina + + HTML5 Shiv - Copyright (c) 2012 Alexander Farkas, Jonathan Neal, Paul Irish, + and John-David Dalton + + jQuery - Copyright (c) 2011 John Resig + + jQuery Bgiframe - Copyright (c) 2010 Brandon Aaron (http://brandonaaron.net) + + jQuery BBQ - Copyright (c) 2010 "Cowboy" Ben Alman + + jQuery Cookie - Copyright (c) 2010 Klaus Hartl + + jQuery Form - Copyright (c) 2011 Mike Alsup + + jQuery Globalize - Copyright (c) 2012 Software Freedom Conservancy, Inc. + + jQuery Mousewheel - Copyright (c) 2010 Brandon Aaron + (http://brandonaaron.net) + + jQuery Metadata - Copyright (c) 2006 John Resig, Yehuda Katz, Jörn Zaefferer, + Paul McLanahan + + jQuery Once - Copyright (c) 2009 Konstantin Käfer + + jQuery UI - Copyright (c) 2012 by the original authors + (http://jqueryui.com/about) + + Sizzle.js - Copyright (c) 2011 The Dojo Foundation (http://sizzlejs.com/) + +PHP + + ArchiveTar - Copyright (c) 1997 - 2008 Vincent Blavet - Symfony2 - Copyright (c) 2004 - 2011 Fabien Potencier + Symfony2 - Copyright (c) 2004 - 2012 Fabien Potencier + - YUI - Copyright (c) 2010 Yahoo! Inc. + - Zend Framework (1.10dev - 2010-01-24) - Copyright (c) 2005-2010 Zend + Technologies USA Inc. (http://www.zend.com) - Twig - Copyright (c) 2009 - 2012 Fabien Potencier + Twig - Copyright (c) 2009 Twig Team diff --git a/core/includes/ajax.inc b/core/includes/ajax.inc index a72fc5543a9044abb5eac31079a2a5d17819a0fa..328f54cd55d977de1a1d3cd02b1830d06a46d04d 100644 --- a/core/includes/ajax.inc +++ b/core/includes/ajax.inc @@ -276,7 +276,7 @@ function ajax_render($commands = array()) { $extra_commands = array(); if (!empty($styles)) { - $extra_commands[] = ajax_command_prepend('head', $styles); + $extra_commands[] = ajax_command_add_css($styles); } if (!empty($scripts_header)) { $extra_commands[] = ajax_command_prepend('head', $scripts_header); @@ -1186,3 +1186,26 @@ function ajax_command_restripe($selector) { 'selector' => $selector, ); } + +/** + * Creates a Drupal Ajax 'add_css' command. + * + * This method will add css via ajax in a cross-browser compatible way. + * + * This command is implemented by Drupal.ajax.prototype.commands.add_css() + * defined in misc/ajax.js. + * + * @param $styles + * A string that contains the styles to be added. + * + * @return + * An array suitable for use with the ajax_render() function. + * + * @see misc/ajax.js + */ +function ajax_command_add_css($styles) { + return array( + 'command' => 'add_css', + 'data' => $styles, + ); +} diff --git a/core/includes/bootstrap.inc b/core/includes/bootstrap.inc index 0551e56aa0d9e45051e82c871a6c4107dbc3707e..95adc950ab34aa4f2162ec3ce09779ace6ee08b8 100644 --- a/core/includes/bootstrap.inc +++ b/core/includes/bootstrap.inc @@ -3512,6 +3512,7 @@ function drupal_check_memory_limit($required, $memory_limit = NULL) { // There is sufficient memory if: // - No memory limit is set. // - The memory limit is set to unlimited (-1). - // - The memory limit is greater than the memory required for the operation. - return ((!$memory_limit) || ($memory_limit == -1) || (parse_size($memory_limit) > parse_size($required))); + // - The memory limit is greater than or equal to the memory required for + // the operation. + return ((!$memory_limit) || ($memory_limit == -1) || (parse_size($memory_limit) >= parse_size($required))); } diff --git a/core/includes/common.inc b/core/includes/common.inc index 8b53cc601c683c9515d6a3c775e6eec4bc5d72cb..0b95dcd8dbf9a744d45d2797ee2394f0b7489d86 100644 --- a/core/includes/common.inc +++ b/core/includes/common.inc @@ -3304,7 +3304,11 @@ function drupal_pre_render_styles($elements) { $import_batch = array_slice($import, 0, 31); $import = array_slice($import, 31); $element = $style_element_defaults; - $element['#value'] = implode("\n", $import_batch); + // This simplifies the JavaScript regex, allowing each line + // (separated by \n) to be treated as a completely different string. + // This means that we can use ^ and $ on one line at a time, and not + // worry about style tags since they'll never match the regex. + $element['#value'] = "\n" . implode("\n", $import_batch) . "\n"; $element['#attributes']['media'] = $group['media']; $element['#browsers'] = $group['browsers']; $elements[] = $element; @@ -6949,6 +6953,9 @@ function drupal_common_theme() { 'range' => array( 'render element' => 'element', ), + 'color' => array( + 'render element' => 'element', + ), 'form' => array( 'render element' => 'element', ), diff --git a/core/includes/form.inc b/core/includes/form.inc index 571a43921638f58810f3eceb98d0cf60ffc59680..395cca75beefaca8f96420c93310a4c65726ef19 100644 --- a/core/includes/form.inc +++ b/core/includes/form.inc @@ -5,6 +5,8 @@ * Functions for form and batch generation and processing. */ +use Drupal\Core\Utility\Color; + /** * @defgroup forms Form builder functions * @{ @@ -4180,6 +4182,47 @@ function form_validate_url(&$element, &$form_state) { } } +/** + * Form element validation handler for #type 'color'. + */ +function form_validate_color(&$element, &$form_state) { + $value = trim($element['#value']); + + // Default to black if no value is given. + // @see http://www.w3.org/TR/html5/number-state.html#color-state + if ($value === '') { + form_set_value($element, '#000000', $form_state); + } + else { + // Try to parse the value and normalize it. + try { + form_set_value($element, Color::rgbToHex(Color::hexToRgb($value)), $form_state); + } + catch (InvalidArgumentException $e) { + form_error($element, t('%name must be a valid color.', array('%name' => empty($element['#title']) ? $element['#parents'][0] : $element['#title']))); + } + } +} + +/** + * Returns HTML for a color form element. + * + * @param $variables + * An associative array containing: + * - element: An associative array containing the properties of the element. + * Properties used: #title, #value, #description, #attributes. + * + * @ingroup themeable + */ +function theme_color($variables) { + $element = $variables['element']; + $element['#attributes']['type'] = 'color'; + element_set_attributes($element, array('id', 'name', 'value')); + _form_set_class($element, array('form-color')); + + return '<input' . drupal_attributes($element['#attributes']) . ' />' . drupal_render_children($element); +} + /** * Returns HTML for a form. * diff --git a/core/includes/install.inc b/core/includes/install.inc index 1fb9a3e15113f8c1e1fa6715cda8e649503868e0..41d244a549c86cf7243397b2d9eafa523a0189ae 100644 --- a/core/includes/install.inc +++ b/core/includes/install.inc @@ -327,79 +327,6 @@ function drupal_install_system() { config_install_default_config('system'); } -/** - * Uninstalls a given list of modules. - * - * @param $module_list - * The modules to uninstall. - * @param $uninstall_dependents - * If TRUE, the function will check that all modules which depend on the - * passed-in module list either are already uninstalled or contained in the - * list, and it will ensure that the modules are uninstalled in the correct - * order. This incurs a significant performance cost, so use FALSE if you - * know $module_list is already complete and in the correct order. - * - * @return - * FALSE if one or more dependent modules are missing from the list, TRUE - * otherwise. - */ -function drupal_uninstall_modules($module_list = array(), $uninstall_dependents = TRUE) { - if ($uninstall_dependents) { - // Get all module data so we can find dependents and sort. - $module_data = system_rebuild_module_data(); - // Create an associative array with weights as values. - $module_list = array_flip(array_values($module_list)); - - $profile = drupal_get_profile(); - while (list($module) = each($module_list)) { - if (!isset($module_data[$module]) || drupal_get_installed_schema_version($module) == SCHEMA_UNINSTALLED) { - // This module doesn't exist or is already uninstalled, skip it. - unset($module_list[$module]); - continue; - } - $module_list[$module] = $module_data[$module]->sort; - - // If the module has any dependents which are not already uninstalled and - // not included in the passed-in list, abort. It is not safe to uninstall - // them automatically because uninstalling a module is a destructive - // operation. - foreach (array_keys($module_data[$module]->required_by) as $dependent) { - if (!isset($module_list[$dependent]) && drupal_get_installed_schema_version($dependent) != SCHEMA_UNINSTALLED && $dependent != $profile) { - return FALSE; - } - } - } - - // Sort the module list by pre-calculated weights. - asort($module_list); - $module_list = array_keys($module_list); - } - - $storage = new DatabaseStorage(); - foreach ($module_list as $module) { - // Uninstall the module. - module_load_install($module); - module_invoke($module, 'uninstall'); - drupal_uninstall_schema($module); - - // Remove all configuration belonging to the module. - $config_names = $storage->listAll($module . '.'); - foreach ($config_names as $config_name) { - config($config_name)->delete(); - } - - watchdog('system', '%module module uninstalled.', array('%module' => $module), WATCHDOG_INFO); - drupal_set_installed_schema_version($module, SCHEMA_UNINSTALLED); - } - - if (!empty($module_list)) { - // Call hook_module_uninstall to let other modules act - module_invoke_all('modules_uninstalled', $module_list); - } - - return TRUE; -} - /** * Verify the state of the specified file. * diff --git a/core/includes/module.inc b/core/includes/module.inc index a3de4313400de1b678e889cf3052deb34fef33d2..f40161d420a0a05867df564c3032a399ebe2a74d 100644 --- a/core/includes/module.inc +++ b/core/includes/module.inc @@ -6,6 +6,7 @@ */ use Drupal\Component\Graph\Graph; +use Drupal\Core\Config\DatabaseStorage; /** * Load all the modules that have been enabled in the system table. @@ -579,6 +580,79 @@ function module_disable($module_list, $disable_dependents = TRUE) { } } +/** + * Uninstalls a given list of modules. + * + * @param $module_list + * The modules to uninstall. + * @param $uninstall_dependents + * If TRUE, the function will check that all modules which depend on the + * passed-in module list either are already uninstalled or contained in the + * list, and it will ensure that the modules are uninstalled in the correct + * order. This incurs a significant performance cost, so use FALSE if you + * know $module_list is already complete and in the correct order. + * + * @return + * FALSE if one or more dependent modules are missing from the list, TRUE + * otherwise. + */ +function module_uninstall($module_list = array(), $uninstall_dependents = TRUE) { + if ($uninstall_dependents) { + // Get all module data so we can find dependents and sort. + $module_data = system_rebuild_module_data(); + // Create an associative array with weights as values. + $module_list = array_flip(array_values($module_list)); + + $profile = drupal_get_profile(); + while (list($module) = each($module_list)) { + if (!isset($module_data[$module]) || drupal_get_installed_schema_version($module) == SCHEMA_UNINSTALLED) { + // This module doesn't exist or is already uninstalled, skip it. + unset($module_list[$module]); + continue; + } + $module_list[$module] = $module_data[$module]->sort; + + // If the module has any dependents which are not already uninstalled and + // not included in the passed-in list, abort. It is not safe to uninstall + // them automatically because uninstalling a module is a destructive + // operation. + foreach (array_keys($module_data[$module]->required_by) as $dependent) { + if (!isset($module_list[$dependent]) && drupal_get_installed_schema_version($dependent) != SCHEMA_UNINSTALLED && $dependent != $profile) { + return FALSE; + } + } + } + + // Sort the module list by pre-calculated weights. + asort($module_list); + $module_list = array_keys($module_list); + } + + $storage = new DatabaseStorage(); + foreach ($module_list as $module) { + // Uninstall the module. + module_load_install($module); + module_invoke($module, 'uninstall'); + drupal_uninstall_schema($module); + + // Remove all configuration belonging to the module. + $config_names = $storage->listAll($module . '.'); + foreach ($config_names as $config_name) { + config($config_name)->delete(); + } + + watchdog('system', '%module module uninstalled.', array('%module' => $module), WATCHDOG_INFO); + drupal_set_installed_schema_version($module, SCHEMA_UNINSTALLED); + } + + if (!empty($module_list)) { + // Call hook_module_uninstall to let other modules act + module_invoke_all('modules_uninstalled', $module_list); + } + + return TRUE; +} + /** * @defgroup hooks Hooks * @{ diff --git a/core/lib/Drupal/Core/Utility/Color.php b/core/lib/Drupal/Core/Utility/Color.php new file mode 100644 index 0000000000000000000000000000000000000000..27ba440f30f1e032c5a7067c3980962e67355d48 --- /dev/null +++ b/core/lib/Drupal/Core/Utility/Color.php @@ -0,0 +1,101 @@ +<?php + +/** + * @file + * Definition of Drupal\Core\Utility\Color. + */ + +namespace Drupal\Core\Utility; + +/** + * Performs color conversions. + */ +class Color { + + /** + * Validates whether a hexadecimal color value is syntatically correct. + * + * @param $hex + * The hexadecimal string to validate. May contain a leading '#'. May use + * the shorthand notation (e.g., '123' for '112233'). + * + * @return bool + * TRUE if $hex is valid or FALSE if it is not. + */ + public static function validateHex($hex) { + // Must be a string. + $valid = is_string($hex); + // Hash prefix is optional. + $hex = ltrim($hex, '#'); + // Must be either RGB or RRGGBB. + $length = drupal_strlen($hex); + $valid = $valid && ($length === 3 || $length === 6); + // Must be a valid hex value. + $valid = $valid && ctype_xdigit($hex); + return $valid; + } + + /** + * Parses a hexadecimal color string like '#abc' or '#aabbcc'. + * + * @param string $hex + * The hexadecimal color string to parse. + * + * @return array + * An array containing the values for 'red', 'green', 'blue'. + * + * @throws \InvalidArgumentException + */ + public static function hexToRgb($hex) { + if (!self::validateHex($hex)) { + throw new \InvalidArgumentException("'$hex' is not a valid hex value."); + } + + // Ignore '#' prefixes. + $hex = ltrim($hex, '#'); + + // Convert shorhands like '#abc' to '#aabbcc'. + if (strlen($hex) == 3) { + $hex = $hex[0] . $hex[0] . $hex[1] . $hex[1] . $hex[2] . $hex[2]; + } + + $c = hexdec($hex); + + return array( + 'red' => $c >> 16 & 0xFF, + 'green' => $c >> 8 & 0xFF, + 'blue' => $c & 0xFF, + ); + } + + /** + * Converts RGB color arrays and RGB strings in CSS notation to lowercase + * simple colors like '#aabbcc'. + * + * @param array|string $input + * The value to convert. If the value is an array the first three elements + * will be used as the red, green and blue components. String values in CSS + * notation like '10, 20, 30' are also supported. + * + * @return string + * The lowercase simple color representation of the given color. + */ + public static function rgbToHex($input) { + // Remove named array keys if input comes from Color::hex2rgb(). + if (is_array($input)) { + $rgb = array_values($input); + } + // Parse string input in CSS notation ('10, 20, 30'). + elseif (is_string($input)) { + preg_match('/(\d+), ?(\d+), ?(\d+)/', $input, $rgb); + array_shift($rgb); + } + + $out = 0; + foreach ($rgb as $k => $v) { + $out |= $v << (16 - $k * 8); + } + + return '#' . str_pad(dechex($out), 6, 0, STR_PAD_LEFT); + } +} diff --git a/core/misc/ajax.js b/core/misc/ajax.js index aef9f310d7ae8565da239662ab2ef5b6e75b59ac..71399c4d2171e001af2867e295b26eb5994e3dba 100644 --- a/core/misc/ajax.js +++ b/core/misc/ajax.js @@ -609,6 +609,26 @@ Drupal.ajax.prototype.commands = { .removeClass('odd even') .filter(':even').addClass('odd').end() .filter(':odd').addClass('even'); + }, + + /** + * Command to add css. + * + * Uses the proprietary addImport method if available as browsers which + * support that method ignore @import statements in dynamically added + * stylesheets. + */ + add_css: function (ajax, response, status) { + // Add the styles in the normal way. + $('head').prepend(response.data); + // Add imports in the styles using the addImport method if available. + var match, importMatch = /^@import url\("(.*)"\);$/igm; + if (document.styleSheets[0].addImport && importMatch.test(response.data)) { + importMatch.lastIndex = 0; + while (match = importMatch.exec(response.data)) { + document.styleSheets[0].addImport(match[1]); + } + } } }; diff --git a/core/modules/book/book.module b/core/modules/book/book.module index 6d111d9b9dd77e8b32e17e51fb0d7e1ed2a00465..1087e23af6e5765d3dfcdc877ac5088c22d5f4ac 100644 --- a/core/modules/book/book.module +++ b/core/modules/book/book.module @@ -540,13 +540,12 @@ function _book_add_form_elements(&$form, &$form_state, Node $node) { '#collapsed' => TRUE, '#group' => 'additional_settings', '#attributes' => array( - 'class' => array('book-form'), + 'class' => array('book-outline-form'), ), '#attached' => array( 'js' => array(drupal_get_path('module', 'book') . '/book.js'), ), '#tree' => TRUE, - '#attributes' => array('class' => array('book-outline-form')), ); foreach (array('menu_name', 'mlid', 'nid', 'router_path', 'has_children', 'options', 'module', 'original_bid', 'parent_depth_limit') as $key) { $form['book'][$key] = array( diff --git a/core/modules/entity/lib/Drupal/entity/Tests/EntityFieldQueryTest.php b/core/modules/entity/lib/Drupal/entity/Tests/EntityFieldQueryTest.php index 9b4a91ce27a384e73010a31c5943bdb2a6922857..81bc286d980e75c83692056d8b4db47f10813c13 100644 --- a/core/modules/entity/lib/Drupal/entity/Tests/EntityFieldQueryTest.php +++ b/core/modules/entity/lib/Drupal/entity/Tests/EntityFieldQueryTest.php @@ -27,7 +27,7 @@ public static function getInfo() { } function setUp() { - parent::setUp(array('field_test')); + parent::setUp(array('node', 'field_test', 'entity_query_access_test', 'node_access_test')); field_test_create_bundle('bundle1'); field_test_create_bundle('bundle2'); @@ -1528,6 +1528,26 @@ function testEntityFieldQueryTableSort() { unset($_GET['order']); } + /** + * Tests EntityFieldQuery access on non-node entities. + */ + function testEntityFieldQueryAccess() { + // Test as a user with ability to bypass node access. + $privileged_user = $this->drupalCreateUser(array('bypass node access', 'access content')); + $this->drupalLogin($privileged_user); + $this->drupalGet('entity-query-access/test/' . $this->fields[0]['field_name']); + $this->assertText('Found entity', 'Returned access response with entities.'); + $this->drupalLogout(); + + // Test as a user that does not have ability to bypass node access or view + // all nodes. + $regular_user = $this->drupalCreateUser(array('access content')); + $this->drupalLogin($regular_user); + $this->drupalGet('entity-query-access/test/' . $this->fields[0]['field_name']); + $this->assertText('Found entity', 'Returned access response with entities.'); + $this->drupalLogout(); + } + /** * Fetches the results of an EntityFieldQuery and compares. * diff --git a/core/modules/entity/tests/modules/entity_query_access_test/entity_query_access_test.info b/core/modules/entity/tests/modules/entity_query_access_test/entity_query_access_test.info new file mode 100644 index 0000000000000000000000000000000000000000..369b2048edd9e59fa0d8d592a45ef29b78267083 --- /dev/null +++ b/core/modules/entity/tests/modules/entity_query_access_test/entity_query_access_test.info @@ -0,0 +1,7 @@ +name = "Entity query access test" +description = "Support module for checking entity query results." +package = Testing +version = VERSION +core = 8.x +hidden = TRUE + diff --git a/core/modules/entity/tests/modules/entity_query_access_test/entity_query_access_test.module b/core/modules/entity/tests/modules/entity_query_access_test/entity_query_access_test.module new file mode 100644 index 0000000000000000000000000000000000000000..262f3b75379a875b7207ec51eb56fcf9d1057377 --- /dev/null +++ b/core/modules/entity/tests/modules/entity_query_access_test/entity_query_access_test.module @@ -0,0 +1,57 @@ +<?php + +/** + * @file + * Helper module for testing EntityFieldQuery access on any type of entity. + */ + +use Drupal\entity\EntityFieldQuery; +use Drupal\entity\EntityFieldQueryException; + +/** + * Implements hook_menu(). + */ +function entity_query_access_test_menu() { + $items['entity-query-access/test/%'] = array( + 'title' => "Retrieve a sample of entity query access data", + 'page callback' => 'entity_query_access_test_sample_query', + 'page arguments' => array(2), + 'access callback' => TRUE, + 'type' => MENU_CALLBACK, + ); + + return $items; +} + +/** + * Returns the results from an example EntityFieldQuery. + */ +function entity_query_access_test_sample_query($field_name) { + global $user; + + // Simulate user does not have access to view all nodes. + $access = &drupal_static('node_access_view_all_nodes'); + $access[$user->uid] = FALSE; + + $query = new EntityFieldQuery(); + $query + ->entityCondition('entity_type', 'test_entity_bundle_key') + ->fieldCondition($field_name, 'value', 0, '>') + ->entityOrderBy('entity_id', 'ASC'); + $results = array( + 'items' => array(), + 'title' => t('EntityFieldQuery results'), + ); + foreach ($query->execute() as $entity_type => $entity_ids) { + foreach ($entity_ids as $entity_id => $entity_stub) { + $results['items'][] = format_string('Found entity of type @entity_type with id @entity_id', array('@entity_type' => $entity_type, '@entity_id' => $entity_id)); + } + } + if (count($results['items']) > 0) { + $output = theme('item_list', $results); + } + else { + $output = 'No results found with EntityFieldQuery.'; + } + return $output; +} diff --git a/core/modules/file/file.field.inc b/core/modules/file/file.field.inc index 768ee7d27a60378225b76f40cc7568bd952cf82c..32834f48b98a6f5caeda4b9f68480fc14e390e81 100644 --- a/core/modules/file/file.field.inc +++ b/core/modules/file/file.field.inc @@ -587,10 +587,9 @@ function file_field_widget_process($element, &$form_state, $form) { // Add the description field if enabled. if (!empty($instance['settings']['description_field']) && $item['fid']) { $element['description'] = array( - '#type' => 'textfield', + '#type' => variable_get('file_description_type', 'textfield'), '#title' => t('Description'), '#value' => isset($item['description']) ? $item['description'] : '', - '#type' => variable_get('file_description_type', 'textfield'), '#maxlength' => variable_get('file_description_length', 128), '#description' => t('The description may be used as the label of the link to the file.'), ); diff --git a/core/modules/image/image.admin.css b/core/modules/image/image.admin.css index efe522fc93845d176ccc435206c22d7c4f1be691..2eb9b0d7a951060295af68c6c7438bf2d658235f 100644 --- a/core/modules/image/image.admin.css +++ b/core/modules/image/image.admin.css @@ -2,34 +2,34 @@ /** * Image style configuration pages. */ -div.image-style-new, -div.image-style-new div { +.image-style-new, +.image-style-new div { display: inline; } -div.image-style-preview div.preview-image-wrapper { +.image-style-preview .preview-image-wrapper { float: left; padding-bottom: 2em; text-align: center; top: 50%; width: 48%; } -div.image-style-preview div.preview-image { +.image-style-preview .preview-image { margin: auto; position: relative; } -div.image-style-preview div.preview-image div.width { +.image-style-preview .preview-image .width { border: 1px solid #666; border-top: none; + bottom: -6px; height: 2px; left: -1px; - bottom: -6px; position: absolute; } -div.image-style-preview div.preview-image div.width span { +.image-style-preview .preview-image .width span { position: relative; top: 4px; } -div.image-style-preview div.preview-image div.height { +.image-style-preview .preview-image .height { border: 1px solid #666; border-left: none; position: absolute; @@ -37,7 +37,7 @@ div.image-style-preview div.preview-image div.height { top: -1px; width: 2px; } -div.image-style-preview div.preview-image div.height span { +.image-style-preview .preview-image .height span { height: 2em; left: 10px; margin-top: -1em; @@ -48,13 +48,13 @@ div.image-style-preview div.preview-image div.height span { /** * Image anchor element. */ -table.image-anchor { +.image-anchor { width: auto; } -table.image-anchor tr.even, -table.image-anchor tr.odd { +.image-anchor tr.even, +.image-anchor tr.odd { background: none; } -table.image-anchor td { +.image-anchor td { border: 1px solid #ccc; } diff --git a/core/modules/image/image.field.inc b/core/modules/image/image.field.inc index 2a32c154b8a96d7818f63b8b82dd8575554ddeef..e6dfafa8e4a87ac0ecc6ff5493bdfa417bf3837a 100644 --- a/core/modules/image/image.field.inc +++ b/core/modules/image/image.field.inc @@ -316,7 +316,8 @@ function image_field_widget_settings_form($field, $instance) { */ function image_field_widget_form(&$form, &$form_state, $field, $instance, $langcode, $items, $delta, $element) { - // Add display_field setting to field because file_field_widget_form() assumes it is set. + // Add display_field setting to field because file_field_widget_form() assumes + // it is set. $field['settings']['display_field'] = 0; $elements = file_field_widget_form($form, $form_state, $field, $instance, $langcode, $items, $delta, $element); @@ -365,7 +366,7 @@ function image_field_widget_process($element, &$form_state, $form) { $widget_settings = $instance['widget']['settings']; $element['#theme'] = 'image_widget'; - $element['#attached']['css'][] = drupal_get_path('module', 'image') . '/image.css'; + $element['#attached']['css'][] = drupal_get_path('module', 'image') . '/image.theme.css'; // Add the image preview. if ($element['#file'] && $widget_settings['preview_image_style']) { @@ -435,7 +436,7 @@ function image_field_widget_process($element, &$form_state, $form) { /** * Returns HTML for an image field widget. * - * @param $variables + * @param array $variables * An associative array containing: * - element: A render element representing the image field widget. * @@ -577,7 +578,7 @@ function image_field_formatter_view($entity_type, $entity, $field, $instance, $l /** * Returns HTML for an image field formatter. * - * @param $variables + * @param array $variables * An associative array containing: * - item: An array of image data. * - image_style: An optional image style. @@ -589,7 +590,7 @@ function theme_image_formatter($variables) { $item = $variables['item']; // Do not output an empty 'title' attribute. - if (drupal_strlen($item['title']) == 0) { + if (isset($item['title']) && drupal_strlen($item['title']) == 0) { unset($item['title']); } @@ -601,9 +602,11 @@ function theme_image_formatter($variables) { $output = theme('image', $item); } - if (!empty($variables['path']['path'])) { + // The link path and link options are both optional, but for the options to be + // processed, the link path must at least be an empty string. + if (isset($variables['path']['path'])) { $path = $variables['path']['path']; - $options = $variables['path']['options']; + $options = isset($variables['path']['options']) ? $variables['path']['options'] : array(); // When displaying an image inside a link, the html option must be TRUE. $options['html'] = TRUE; $output = l($output, $path, $options); diff --git a/core/modules/image/image-rtl.css b/core/modules/image/image.theme-rtl.css similarity index 68% rename from core/modules/image/image-rtl.css rename to core/modules/image/image.theme-rtl.css index 2a7a855ede53c25a841bbc191a5fb0aca28af017..facb97bbc6ea0f83331ef49de0e36f4dfbfac2d0 100644 --- a/core/modules/image/image-rtl.css +++ b/core/modules/image/image.theme-rtl.css @@ -2,10 +2,10 @@ /** * Image upload widget. */ -div.image-preview { +.image-preview { float: right; padding: 0 0 10px 10px; } -div.image-widget-data { +.image-widget-data { float: right; } diff --git a/core/modules/image/image.css b/core/modules/image/image.theme.css similarity index 62% rename from core/modules/image/image.css rename to core/modules/image/image.theme.css index 7db307bf07c877b6cc9b51ab48c296bb8248e941..9f7d53419eef7a83bf65e02eb069d824618fbcec 100644 --- a/core/modules/image/image.css +++ b/core/modules/image/image.theme.css @@ -2,13 +2,13 @@ /** * Image upload widget. */ -div.image-preview { +.image-preview { float: left; /* LTR */ padding: 0 10px 10px 0; /* LTR */ } -div.image-widget-data { +.image-widget-data { float: left; /* LTR */ } -div.image-widget-data input.text-field { +.image-widget-data .text-field { width: auto; } diff --git a/core/modules/image/lib/Drupal/image/Tests/ImageThemeFunctionTest.php b/core/modules/image/lib/Drupal/image/Tests/ImageThemeFunctionTest.php new file mode 100644 index 0000000000000000000000000000000000000000..ed01b23d7c7bd87e50f6600d2ed9de6ae00aaaa9 --- /dev/null +++ b/core/modules/image/lib/Drupal/image/Tests/ImageThemeFunctionTest.php @@ -0,0 +1,71 @@ +<?php + +/** + * @file + * Definition of Drupal\image\Tests\ImageThemeFunctionTest. + */ + +namespace Drupal\image\Tests; + +use Drupal\simpletest\WebTestBase; + +/** + * Tests image theme functions. + */ +class ImageThemeFunctionTest extends WebTestBase { + + public static function getInfo() { + return array( + 'name' => 'Image theme functions', + 'description' => 'Tests the image theme functions.', + 'group' => 'Image', + ); + } + + function setUp() { + parent::setUp(array('image')); + } + + /** + * Tests usage of the image field formatters. + */ + function testImageFormatterTheme() { + // Create an image. + $files = $this->drupalGetTestFiles('image'); + $file = reset($files); + $original_uri = file_unmanaged_copy($file->uri, 'public://', FILE_EXISTS_RENAME); + + // Create a style. + image_style_save(array('name' => 'test')); + $url = image_style_url('test', $original_uri); + + // Test using theme_image_formatter() without an image title, alt text, or + // link options. + $path = $this->randomName(); + $element = array( + '#theme' => 'image_formatter', + '#image_style' => 'test', + '#item' => array( + 'uri' => $original_uri, + ), + '#path' => array( + 'path' => $path, + ), + ); + $rendered_element = render($element); + $expected_result = '<a href="' . base_path() . $path . '"><img src="' . $url . '" alt="" /></a>'; + $this->assertEqual($expected_result, $rendered_element, 'theme_image_formatter() correctly renders without title, alt, or path options.'); + + // Link the image to a fragment on the page, and not a full URL. + $fragment = $this->randomName(); + $element['#path']['path'] = ''; + $element['#path']['options'] = array( + 'external' => TRUE, + 'fragment' => $fragment, + ); + $rendered_element = render($element); + $expected_result = '<a href="#' . $fragment . '"><img src="' . $url . '" alt="" /></a>'; + $this->assertEqual($expected_result, $rendered_element, 'theme_image_formatter() correctly renders a link fragment.'); + } + +} diff --git a/core/modules/locale/lib/Drupal/locale/Tests/LocaleUninstallTest.php b/core/modules/locale/lib/Drupal/locale/Tests/LocaleUninstallTest.php index 30595dd9830d7e1090f65b4644923982d27877f5..b63e8cdd895f79e0ef0f4e55dd24ad2a02dd4669 100644 --- a/core/modules/locale/lib/Drupal/locale/Tests/LocaleUninstallTest.php +++ b/core/modules/locale/lib/Drupal/locale/Tests/LocaleUninstallTest.php @@ -87,7 +87,7 @@ function testUninstallProcess() { // Uninstall Locale. module_disable($locale_module); - drupal_uninstall_modules($locale_module); + module_uninstall($locale_module); // Visit the front page. $this->drupalGet(''); diff --git a/core/modules/node/node.module b/core/modules/node/node.module index 3a94397715688e73dde9bc299970d8eaac56e11f..3db5bbd1e8afcf57c86624b1f75f5f8889ab9544 100644 --- a/core/modules/node/node.module +++ b/core/modules/node/node.module @@ -3320,8 +3320,9 @@ function _node_query_node_access_alter($query, $type) { // @endcode // // So instead of directly adding to the query object, we need to collect - // in a separate db_and() object and then at the end add it to the query. - $entity_conditions = db_and(); + // all of the node access conditions in a separate db_and() object and + // then add it to the query at the end. + $node_conditions = db_and(); } foreach ($tables as $nalias => $tableinfo) { $table = $tableinfo['table']; @@ -3355,16 +3356,24 @@ function _node_query_node_access_alter($query, $type) { $field = 'entity_id'; } $subquery->where("$nalias.$field = na.nid"); - $query->exists($subquery); + + // For an entity query, attach the subquery to entity conditions. + if ($type == 'entity') { + $node_conditions->exists($subquery); + } + // Otherwise attach it to the node query itself. + else { + $query->exists($subquery); + } } } if ($type == 'entity' && count($subquery->conditions())) { // All the node access conditions are only for field values belonging to // nodes. - $entity_conditions->condition("$base_alias.entity_type", 'node'); + $node_conditions->condition("$base_alias.entity_type", 'node'); $or = db_or(); - $or->condition($entity_conditions); + $or->condition($node_conditions); // If the field value belongs to a non-node entity type then this function // does not do anything with it. $or->condition("$base_alias.entity_type", 'node', '<>'); diff --git a/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php b/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php index 486dbcb741462ee21dfe111b70bd31e00d972d47..0856611e60c8d0d232538ee72d12ab50b364b277 100644 --- a/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php +++ b/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php @@ -1369,6 +1369,8 @@ protected function drupalPostAJAX($path, $edit, $triggering_element, $ajax_path break; case 'restripe': break; + case 'add_css': + break; } } $content = $dom->saveHTML(); @@ -1463,6 +1465,7 @@ protected function handleForm(&$post, &$edit, &$upload, $submit, $form) { case 'url': case 'number': case 'range': + case 'color': case 'hidden': case 'password': case 'email': diff --git a/core/modules/system/lib/Drupal/system/Tests/Ajax/CommandsTest.php b/core/modules/system/lib/Drupal/system/Tests/Ajax/CommandsTest.php index 299b674583f6e3246b028694c0fd3583c6f8163c..faa35bbbe3a3ea177ee2ecce007b1d81e101b693 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Ajax/CommandsTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Ajax/CommandsTest.php @@ -157,5 +157,13 @@ function testAjaxCommands() { 'settings' => array('ajax_forms_test' => array('foo' => 42)), ); $this->assertCommand($commands, $expected, "'settings' AJAX command issued with correct data"); + + // Tests the 'add_css' command. + $commands = $this->drupalPostAJAX($form_path, $edit, array('op' => t("AJAX 'add_css' command"))); + $expected = array( + 'command' => 'add_css', + 'data' => 'my/file.css', + ); + $this->assertCommand($commands, $expected, "'add_css' AJAX command issued with correct data"); } } diff --git a/core/modules/system/lib/Drupal/system/Tests/Batch/PercentagesUnitTest.php b/core/modules/system/lib/Drupal/system/Tests/Batch/PercentagesUnitTest.php index a51b1f9b69437d600887b9f0908ffa5d92152242..a99262655a98064c78dfe8ea70873f825a2ccca3 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Batch/PercentagesUnitTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Batch/PercentagesUnitTest.php @@ -40,8 +40,6 @@ function setUp() { '33' => array('total' => 3, 'current' => 1), // 2/3 is closer to 67% than to 66%. '67' => array('total' => 3, 'current' => 2), - // A full 3/3 should equal 100%. - '100' => array('total' => 3, 'current' => 3), // 1/199 should round up to 1%. '1' => array('total' => 199, 'current' => 1), // 198/199 should round down to 99%. diff --git a/core/modules/system/lib/Drupal/system/Tests/Bootstrap/MiscUnitTest.php b/core/modules/system/lib/Drupal/system/Tests/Bootstrap/MiscUnitTest.php index fe4cdcb172de4d841ad0eafc46bf0e3d91513965..daa7d594c84b84a7723f78aab79ef9112207c396 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Bootstrap/MiscUnitTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Bootstrap/MiscUnitTest.php @@ -44,13 +44,14 @@ function testCheckMemoryLimit() { // Get the available memory and multiply it by two to make it unreasonably // high. $twice_avail_memory = ($memory_limit * 2) . 'MB'; - $this->assertFalse(drupal_check_memory_limit($twice_avail_memory), 'drupal_check_memory_limit() returns FALSE for twice the available memory limit.'); - // The function should always return true if the memory limit is set to -1. $this->assertTrue(drupal_check_memory_limit($twice_avail_memory, -1), 'drupal_check_memory_limit() returns TRUE when a limit of -1 (none) is supplied'); // Test that even though we have 30MB of memory available - the function // returns FALSE when given an upper limit for how much memory can be used. $this->assertFalse(drupal_check_memory_limit('30MB', '16MB'), 'drupal_check_memory_limit() returns FALSE with a 16MB upper limit on a 30MB requirement.'); + + // Test that an equal amount of memory to the amount requested returns TRUE. + $this->assertTrue(drupal_check_memory_limit('30MB', '30MB'), 'drupal_check_memory_limit() returns TRUE when requesting 30MB on a 30MB requirement.'); } } diff --git a/core/modules/system/lib/Drupal/system/Tests/Common/CascadingStylesheetsTest.php b/core/modules/system/lib/Drupal/system/Tests/Common/CascadingStylesheetsTest.php index 441a2ba3c67b6d3861d8106053fc8c7408d14cdf..397762bf4de6a8eae22d6798843a8334eec1cacc 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Common/CascadingStylesheetsTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Common/CascadingStylesheetsTest.php @@ -89,6 +89,10 @@ function testRenderFile() { drupal_add_css($css); $styles = drupal_get_css(); $this->assertTrue(strpos($styles, $css) > 0, t('Rendered CSS includes the added stylesheet.')); + // Verify that newlines are properly added inside style tags. + $query_string = variable_get('css_js_query_string', '0'); + $css_processed = "<style media=\"all\">\n@import url(\"" . check_plain(file_create_url($css)) . "?" . $query_string ."\");\n</style>"; + $this->assertEqual(trim($styles), $css_processed, t('Rendered CSS includes newlines inside style tags for JavaScript use.')); } /** diff --git a/core/modules/system/lib/Drupal/system/Tests/Common/ColorTest.php b/core/modules/system/lib/Drupal/system/Tests/Common/ColorTest.php new file mode 100644 index 0000000000000000000000000000000000000000..2c32b31e88951570bac7e23c96e0a8a208fbc95e --- /dev/null +++ b/core/modules/system/lib/Drupal/system/Tests/Common/ColorTest.php @@ -0,0 +1,100 @@ +<?php + +/** + * @file + * Definition of Drupal\system\Tests\Common\ColorTest. + */ + +namespace Drupal\system\Tests\Common; + +use Drupal\Core\Utility\Color; +use Drupal\simpletest\UnitTestBase; + +/** + * Tests color conversion functions. + */ +class ColorTest extends UnitTestBase { + + public static function getInfo() { + return array( + 'name' => 'Color conversion', + 'description' => 'Tests Color utility class conversions.', + 'group' => 'Common', + ); + } + + /** + * Tests Color::hexToRgb(). + */ + function testHexToRgb() { + // Any invalid arguments should throw an exception. + $values = array('', '-1', '1', '12', '12345', '1234567', '123456789', '123456789a', 'foo'); + // Duplicate all invalid value tests with additional '#' prefix. + // The '#' prefix inherently turns the data type into a string. + foreach ($values as $value) { + $values[] = '#' . $value; + } + // Add invalid data types (hex value must be a string). + $values = array_merge($values, array( + 1, 12, 1234, 12345, 123456, 1234567, 12345678, 123456789, 123456789, + -1, PHP_INT_MAX, PHP_INT_MAX + 1, -PHP_INT_MAX, + 0x0, 0x010, + )); + + foreach ($values as $test) { + $this->assertFalse(Color::validateHex($test), var_export($test, TRUE) . ' is invalid.'); + try { + Color::hexToRgb($test); + $this->fail('Color::hexToRgb(' . var_export($test, TRUE) . ') did not throw an exception.'); + } + catch (\InvalidArgumentException $e) { + $this->pass('Color::hexToRgb(' . var_export($test, TRUE) . ') threw an exception.'); + } + } + + // PHP automatically casts a numeric array key into an integer. + // Since hex values may consist of 0-9 only, they need to be defined as + // array values. + $tests = array( + // Shorthands without alpha. + array('hex' => '#000', 'rgb' => array('red' => 0, 'green' => 0, 'blue' => 0)), + array('hex' => '#fff', 'rgb' => array('red' => 255, 'green' => 255, 'blue' => 255)), + array('hex' => '#abc', 'rgb' => array('red' => 170, 'green' => 187, 'blue' => 204)), + array('hex' => 'cba', 'rgb' => array('red' => 204, 'green' => 187, 'blue' => 170)), + // Full without alpha. + array('hex' => '#000000', 'rgb' => array('red' => 0, 'green' => 0, 'blue' => 0)), + array('hex' => '#ffffff', 'rgb' => array('red' => 255, 'green' => 255, 'blue' => 255)), + array('hex' => '#010203', 'rgb' => array('red' => 1, 'green' => 2, 'blue' => 3)), + ); + foreach ($tests as $test) { + $result = Color::hexToRgb($test['hex']); + $this->assertIdentical($result, $test['rgb']); + } + } + + /** + * Tests Color::rgbToHex(). + */ + function testRgbToHex() { + $tests = array( + '#000000' => array('red' => 0, 'green' => 0, 'blue' => 0), + '#ffffff' => array('red' => 255, 'green' => 255, 'blue' => 255), + '#777777' => array('red' => 119, 'green' => 119, 'blue' => 119), + '#010203' => array('red' => 1, 'green' => 2, 'blue' => 3), + ); + // Input using named RGB array (e.g., as returned by Color::hexToRgb()). + foreach ($tests as $expected => $rgb) { + $this->assertIdentical(Color::rgbToHex($rgb), $expected); + } + // Input using indexed RGB array (e.g.: array(10, 10, 10)). + foreach ($tests as $expected => $rgb) { + $rgb = array_values($rgb); + $this->assertIdentical(Color::rgbToHex($rgb), $expected); + } + // Input using CSS RGB string notation (e.g.: 10, 10, 10). + foreach ($tests as $expected => $rgb) { + $rgb = implode(', ', $rgb); + $this->assertIdentical(Color::rgbToHex($rgb), $expected); + } + } +} diff --git a/core/modules/system/lib/Drupal/system/Tests/Form/FormTest.php b/core/modules/system/lib/Drupal/system/Tests/Form/FormTest.php index 20d1f56e79b4f302d718259f348da814b4042725..6225bb9d5f0fcd38c3504a38f5efe9518f0f09c4 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Form/FormTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Form/FormTest.php @@ -75,7 +75,7 @@ function testRequiredFields() { $elements['radios']['element'] = array('#title' => $this->randomName(), '#type' => 'radios', '#options' => array('' => t('None'), $this->randomName(), $this->randomName(), $this->randomName())); $elements['radios']['empty_values'] = $empty_arrays; - $elements['checkbox']['element'] = array('#title' => $this->randomName(), '#type' => 'checkbox', '#required' => TRUE, '#title' => $this->randomName()); + $elements['checkbox']['element'] = array('#title' => $this->randomName(), '#type' => 'checkbox', '#required' => TRUE); $elements['checkbox']['empty_values'] = $empty_checkbox; $elements['checkboxes']['element'] = array('#title' => $this->randomName(), '#type' => 'checkboxes', '#options' => array($this->randomName(), $this->randomName(), $this->randomName())); @@ -383,6 +383,41 @@ function testRange() { $this->assertFieldByXPath('//input[@type="range" and contains(@class, "error")]', NULL, 'Range element has the error class.'); } + /** + * Tests validation of #type 'color' elements. + */ + function testColorValidation() { + // Keys are inputs, values are expected results. + $values = array( + '' => '#000000', + '#000' => '#000000', + 'AAA' => '#aaaaaa', + '#af0DEE' => '#af0dee', + '#99ccBc' => '#99ccbc', + '#aabbcc' => '#aabbcc', + '123456' => '#123456', + ); + + // Tests that valid values are properly normalized. + foreach ($values as $input => $expected) { + $edit = array( + 'color' => $input, + ); + $result = json_decode($this->drupalPost('form-test/color', $edit, 'Submit')); + $this->assertEqual($result->color, $expected); + } + + // Tests invalid values are rejected. + $values = array('#0008', '#1234', '#fffffg', '#abcdef22', '17', '#uaa'); + foreach ($values as $input) { + $edit = array( + 'color' => $input, + ); + $this->drupalPost('form-test/color', $edit, 'Submit'); + $this->assertRaw(t('%name must be a valid color.', array('%name' => 'Color'))); + } + } + /** * Test handling of disabled elements. * @@ -426,7 +461,7 @@ function testDisabledElements() { // All the elements should be marked as disabled, including the ones below // the disabled container. - $this->assertEqual(count($disabled_elements), 39, 'The correct elements have the disabled property in the HTML code.'); + $this->assertEqual(count($disabled_elements), 40, 'The correct elements have the disabled property in the HTML code.'); $this->drupalPost(NULL, $edit, t('Submit')); $returned_values['hijacked'] = drupal_json_decode($this->content); diff --git a/core/modules/system/lib/Drupal/system/Tests/Module/ModuleApiTest.php b/core/modules/system/lib/Drupal/system/Tests/Module/ModuleApiTest.php index c63a7b20ec6180770c13beae978e7174af540215..8468185a3878564f174e5e8185248c0f2d88d5f2 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Module/ModuleApiTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Module/ModuleApiTest.php @@ -205,8 +205,8 @@ function testDependencyResolution() { // Try to uninstall the PHP module by itself. This should be rejected, // since the modules which it depends on need to be uninstalled first, and // that is too destructive to perform automatically. - $result = drupal_uninstall_modules(array('php')); - $this->assertFalse($result, t('Calling drupal_uninstall_modules() on a module whose dependents are not uninstalled fails.')); + $result = module_uninstall(array('php')); + $this->assertFalse($result, t('Calling module_uninstall() on a module whose dependents are not uninstalled fails.')); foreach (array('forum', 'poll', 'php') as $module) { $this->assertNotEqual(drupal_get_installed_schema_version($module), SCHEMA_UNINSTALLED, t('The @module module was not uninstalled.', array('@module' => $module))); } @@ -214,17 +214,17 @@ function testDependencyResolution() { // Now uninstall all three modules explicitly, but in the incorrect order, // and make sure that drupal_uninstal_modules() uninstalled them in the // correct sequence. - $result = drupal_uninstall_modules(array('poll', 'php', 'forum')); - $this->assertTrue($result, t('drupal_uninstall_modules() returns the correct value.')); + $result = module_uninstall(array('poll', 'php', 'forum')); + $this->assertTrue($result, t('module_uninstall() returns the correct value.')); foreach (array('forum', 'poll', 'php') as $module) { $this->assertEqual(drupal_get_installed_schema_version($module), SCHEMA_UNINSTALLED, t('The @module module was uninstalled.', array('@module' => $module))); } - $this->assertEqual(variable_get('test_module_uninstall_order', array()), array('forum', 'poll', 'php'), t('Modules were uninstalled in the correct order by drupal_uninstall_modules().')); + $this->assertEqual(variable_get('test_module_uninstall_order', array()), array('forum', 'poll', 'php'), t('Modules were uninstalled in the correct order by module_uninstall().')); // Uninstall the profile module from above, and make sure that the profile // itself is not on the list of dependent modules to be uninstalled. - $result = drupal_uninstall_modules(array('comment')); - $this->assertTrue($result, t('drupal_uninstall_modules() returns the correct value.')); + $result = module_uninstall(array('comment')); + $this->assertTrue($result, t('module_uninstall() returns the correct value.')); $this->assertEqual(drupal_get_installed_schema_version('comment'), SCHEMA_UNINSTALLED, t('Comment module was uninstalled.')); $uninstalled_modules = variable_get('test_module_uninstall_order', array()); $this->assertTrue(in_array('comment', $uninstalled_modules), t('Comment module is in the list of uninstalled modules.')); diff --git a/core/modules/system/lib/Drupal/system/Tests/Module/UninstallTest.php b/core/modules/system/lib/Drupal/system/Tests/Module/UninstallTest.php index bc9556f41f14ac28ffc326238e601b77cc5fe96e..98bed37e5b6b7aea22998e7fc344e7106d2de38a 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Module/UninstallTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Module/UninstallTest.php @@ -32,7 +32,7 @@ function testUserPermsUninstalled() { // Uninstalls the module_test module, so hook_modules_uninstalled() // is executed. module_disable(array('module_test')); - drupal_uninstall_modules(array('module_test')); + module_uninstall(array('module_test')); // Are the perms defined by module_test removed from {role_permission}. $count = db_query("SELECT COUNT(rid) FROM {role_permission} WHERE permission = :perm", array(':perm' => 'module_test perm'))->fetchField(); diff --git a/core/modules/system/page.tpl.php b/core/modules/system/page.tpl.php index d45e2d92b568c492a21d50108ad5ac47f0b23fa9..c94f9443eaa9132d2b06fd0bb64b392d0465305a 100644 --- a/core/modules/system/page.tpl.php +++ b/core/modules/system/page.tpl.php @@ -106,8 +106,8 @@ <?php if ($main_menu || $secondary_menu): ?> <nav role="navigation"> - <?php print theme('links__system_main_menu', array('links' => $main_menu, 'attributes' => array('id' => 'main-menu', 'class' => array('links', 'inline', 'clearfix')), 'heading' => t('Main menu'))); ?> - <?php print theme('links__system_secondary_menu', array('links' => $secondary_menu, 'attributes' => array('id' => 'secondary-menu', 'class' => array('links', 'inline', 'clearfix')), 'heading' => t('Secondary menu'))); ?> + <?php print theme('links__system_main_menu', array('links' => $main_menu, 'attributes' => array('id' => 'main-menu', 'class' => array('links', 'inline', 'clearfix')), 'heading' => array('text' => t('Main menu'), 'class' => array('element-invisible')))); ?> + <?php print theme('links__system_secondary_menu', array('links' => $secondary_menu, 'attributes' => array('id' => 'secondary-menu', 'class' => array('links', 'inline', 'clearfix')), 'heading' => array('text' => t('Secondary menu'), 'class' => array('element-invisible')))); ?> </nav> <?php endif; ?> diff --git a/core/modules/system/system.admin.inc b/core/modules/system/system.admin.inc index a60c3fb5b544565666417b281b36e134b8384fcf..f11f15b8f0c4ef19748c2bdc0b9e501fda4974f1 100644 --- a/core/modules/system/system.admin.inc +++ b/core/modules/system/system.admin.inc @@ -1343,7 +1343,7 @@ function system_modules_uninstall_submit($form, &$form_state) { if (!empty($form['#confirmed'])) { // Call the uninstall routine for each selected module. $modules = array_keys($form_state['values']['uninstall']); - drupal_uninstall_modules($modules); + module_uninstall($modules); drupal_set_message(t('The selected modules have been uninstalled.')); $form_state['redirect'] = 'admin/modules/uninstall'; diff --git a/core/modules/system/system.install b/core/modules/system/system.install index 088d464293dab1eb4ad5c424aadfe755a94a8a9a..fd1fa4b60f172a06004f73bb938f9522d400bbf1 100644 --- a/core/modules/system/system.install +++ b/core/modules/system/system.install @@ -222,7 +222,7 @@ function system_requirements($phase) { $description .= ' ' . $t('Contact your system administrator or hosting provider for assistance with increasing your PHP memory limit.'); } - $requirements['php_memory_limit']['description'] = $description . ' ' . $t('See the <a href="@url">Drupal requirements</a> for more information.', array('@url' => 'http://drupal.org/requirements')); + $requirements['php_memory_limit']['description'] = $description . ' ' . $t('For more information, see the online handbook entry for <a href="@memory-limit">increasing the PHP memory limit</a>.', array('@memory-limit' => 'http://drupal.org/node/207036')); $requirements['php_memory_limit']['severity'] = REQUIREMENT_WARNING; } } diff --git a/core/modules/system/system.module b/core/modules/system/system.module index 75b1fa25061a4bdd096391d69f05266f68d26765..a76e3ae35d3cfe1cb95074d57cffc98fbb1c0104 100644 --- a/core/modules/system/system.module +++ b/core/modules/system/system.module @@ -139,7 +139,7 @@ function system_help($path, $arg) { case 'admin/config/people/ip-blocking': return '<p>' . t('IP addresses listed here are blocked from your site. Blocked addresses are completely forbidden from accessing the site and instead see a brief message explaining the situation.') . '</p>'; case 'admin/reports/status': - return '<p>' . t("Here you can find a short overview of your site's parameters as well as any problems detected with your installation. It may be useful to copy and paste this information into support requests filed on drupal.org's support forums and project issue queues.") . '</p>'; + return '<p>' . t("Here you can find a short overview of your site's parameters as well as any problems detected with your installation. It may be useful to copy and paste this information into support requests filed on drupal.org's support forums and project issue queues. Before filing a support request, ensure that your web server meets the <a href=\"@system-requirements\">system requirements.</a>", array('@system-requirements' => 'http://drupal.org/requirements')) . '</p>'; } } @@ -425,6 +425,13 @@ function system_element_info() { '#theme' => 'range', '#theme_wrappers' => array('form_element'), ); + $types['color'] = array( + '#input' => TRUE, + '#process' => array('ajax_process_form'), + '#element_validate' => array('form_validate_color'), + '#theme' => 'color', + '#theme_wrappers' => array('form_element'), + ); $types['machine_name'] = array( '#input' => TRUE, '#default_value' => NULL, diff --git a/core/modules/system/tests/modules/ajax_forms_test/ajax_forms_test.module b/core/modules/system/tests/modules/ajax_forms_test/ajax_forms_test.module index 6a95710a80ad9360b70546ff0230d2695cdb65d1..d4074d1f82139f5428f1e8971ae243981edb68a4 100644 --- a/core/modules/system/tests/modules/ajax_forms_test/ajax_forms_test.module +++ b/core/modules/system/tests/modules/ajax_forms_test/ajax_forms_test.module @@ -258,6 +258,15 @@ function ajax_forms_test_ajax_commands_form($form, &$form_state) { ), ); + // Shows the Ajax 'add_css' command. + $form['add_css_command_example'] = array( + '#type' => 'submit', + '#value' => t("AJAX 'add_css' command"), + '#ajax' => array( + 'callback' => 'ajax_forms_test_advanced_commands_add_css_callback', + ), + ); + $form['submit'] = array( '#type' => 'submit', '#value' => t('Submit'), @@ -410,6 +419,15 @@ function ajax_forms_test_advanced_commands_settings_callback($form, $form_state) return array('#type' => 'ajax', '#commands' => $commands); } +/** + * Ajax callback for 'add_css'. + */ +function ajax_forms_test_advanced_commands_add_css_callback($form, $form_state) { + $commands = array(); + $commands[] = ajax_command_add_css('my/file.css'); + return array('#type' => 'ajax', '#commands' => $commands); +} + /** * This form and its related submit and callback functions demonstrate * not validating another form element when a single Ajax element is triggered. diff --git a/core/modules/system/tests/modules/form_test/form_test.module b/core/modules/system/tests/modules/form_test/form_test.module index 1e9e8820a6494e8737fe3fff8fc02399f86d52bd..ff6d54474a2e4d2ed29eda3ee8438a88d39de588 100644 --- a/core/modules/system/tests/modules/form_test/form_test.module +++ b/core/modules/system/tests/modules/form_test/form_test.module @@ -163,6 +163,12 @@ function form_test_menu() { 'page arguments' => array('form_test_range_invalid'), 'access callback' => TRUE, ); + $items['form-test/color'] = array( + 'title' => 'Color', + 'page callback' => 'drupal_get_form', + 'page arguments' => array('form_test_color'), + 'access callback' => TRUE, + ); $items['form-test/checkboxes-radios'] = array( 'title' => t('Checkboxes, Radios'), 'page callback' => 'drupal_get_form', @@ -1035,6 +1041,8 @@ function form_test_form_state_values_clean_advanced_form_submit($form, &$form_st * Build a form to test a checkbox. */ function _form_test_checkbox($form, &$form_state) { + $form['#submit'] = array('_form_test_submit_values_json'); + // A required checkbox. $form['required_checkbox'] = array( '#type' => 'checkbox', @@ -1098,20 +1106,12 @@ function _form_test_checkbox($form, &$form_state) { return $form; } -/** - * Return the form values via JSON. - */ -function _form_test_checkbox_submit($form, &$form_state) { - // This won't have a proper JSON header, but Drupal doesn't check for that - // anyway so this is fine until it's replaced with a JsonResponse. - print drupal_json_encode($form_state['values']); - exit(); -} - /** * Builds a form to test #type 'select' validation. */ function form_test_select($form, &$form_state) { + $form['#submit'] = array('_form_test_submit_values_json'); + $base = array( '#type' => 'select', '#options' => drupal_map_assoc(array('one', 'two', 'three')), @@ -1209,16 +1209,6 @@ function form_test_select($form, &$form_state) { return $form; } -/** - * Form submit handler for form_test_select(). - */ -function form_test_select_submit($form, &$form_state) { - // This won't have a proper JSON header, but Drupal doesn't check for that - // anyway so this is fine until it's replaced with a JsonResponse. - print drupal_json_encode($form_state['values']); - exit(); -} - /** * Builds a form to test #type 'number' and 'range' validation. * @@ -1344,6 +1334,8 @@ function form_test_number($form, &$form_state, $element = 'number') { * @ingroup forms */ function form_test_range($form, &$form_state) { + $form['#submit'] = array('_form_test_submit_values_json'); + $form['with_default_value'] = array( '#type' => 'range', '#title' => 'Range with default value', @@ -1383,16 +1375,6 @@ function form_test_range($form, &$form_state) { return $form; } -/** - * Form submission handler for form_test_range(). - */ -function form_test_range_submit($form, &$form_state) { - // This won't have a proper JSON header, but Drupal doesn't check for that - // anyway so this is fine until it's replaced with a JsonResponse. - print drupal_json_encode($form_state['values']); - exit; -} - /** * Form constructor for testing invalid #type 'range' elements. * @@ -1413,6 +1395,26 @@ function form_test_range_invalid($form, &$form_state) { return $form; } +/** + * Form constructor for testing #type 'color' elements. + * + * @see form_test_color_submit() + * @ingroup forms + */ +function form_test_color($form, &$form_state) { + $form['#submit'] = array('_form_test_submit_values_json'); + + $form['color'] = array( + '#type' => 'color', + '#title' => 'Color', + ); + $form['submit'] = array( + '#type' => 'submit', + '#value' => 'Submit', + ); + return $form; +} + /** * Builds a form to test the placeholder attribute. */ @@ -1494,6 +1496,7 @@ function form_test_checkboxes_radios($form, &$form_state, $customize = FALSE) { * @ingroup forms */ function form_test_email($form, &$form_state) { + $form['#submit'] = array('_form_test_submit_values_json'); $form['email'] = array( '#type' => 'email', '#title' => 'E-Mail address', @@ -1512,16 +1515,6 @@ function form_test_email($form, &$form_state) { return $form; } -/** - * Form submission handler for form_test_email(). - */ -function form_test_email_submit($form, &$form_state) { - // This won't have a proper JSON header, but Drupal doesn't check for that - // anyway so this is fine until it's replaced with a JsonResponse. - print drupal_json_encode($form_state['values']); - exit(); -} - /** * Form constructor for testing #type 'url' elements. * @@ -1529,6 +1522,7 @@ function form_test_email_submit($form, &$form_state) { * @ingroup forms */ function form_test_url($form, &$form_state) { + $form['#submit'] = array('_form_test_submit_values_json'); $form['url'] = array( '#type' => 'url', '#title' => 'Optional URL', @@ -1547,20 +1541,12 @@ function form_test_url($form, &$form_state) { return $form; } -/** - * Form submission handler for form_test_url(). - */ -function form_test_url_submit($form, &$form_state) { - // This won't have a proper JSON header, but Drupal doesn't check for that - // anyway so this is fine until it's replaced with a JsonResponse. - print drupal_json_encode($form_state['values']); - exit(); -} - /** * Build a form to test disabled elements. */ function _form_test_disabled_elements($form, &$form_state) { + $form['#submit'] = array('_form_test_submit_values_json'); + // Elements that take a simple default value. foreach (array('textfield', 'textarea', 'search', 'tel', 'hidden') as $type) { $form[$type] = array( @@ -1637,6 +1623,16 @@ function _form_test_disabled_elements($form, &$form_state) { ); } + // Color. + $form['color'] = array( + '#type' => 'color', + '#title' => 'color', + '#disabled' => TRUE, + '#default_value' => '#0000ff', + '#test_hijack_value' => '#ff0000', + '#disabled' => TRUE, + ); + // Date. $form['date'] = array( '#type' => 'date', @@ -1749,20 +1745,12 @@ function _form_test_disabled_elements($form, &$form_state) { return $form; } -/** - * Return the form values via JSON. - */ -function _form_test_disabled_elements_submit($form, &$form_state) { - // This won't have a proper JSON header, but Drupal doesn't check for that - // anyway so this is fine until it's replaced with a JsonResponse. - print drupal_json_encode($form_state['values']); - exit(); -} - /** * Build a form to test input forgery of enabled elements. */ function _form_test_input_forgery($form, &$form_state) { + $form['#submit'] = array('_form_test_submit_values_json'); + // For testing that a user can't submit a value not matching one of the // allowed options. $form['checkboxes'] = array( @@ -1772,7 +1760,6 @@ function _form_test_input_forgery($form, &$form_state) { 'two' => 'Two', ), ); - $form['submit'] = array( '#type' => 'submit', '#value' => t('Submit'), @@ -1780,16 +1767,6 @@ function _form_test_input_forgery($form, &$form_state) { return $form; } -/** - * Return the form values via JSON. - */ -function _form_test_input_forgery_submit($form, &$form_state) { - // This won't have a proper JSON header, but Drupal doesn't check for that - // anyway so this is fine until it's replaced with a JsonResponse. - print drupal_json_encode($form_state['values']); - exit(); -} - /** * Form builder for testing preservation of values during a rebuild. */ @@ -2156,7 +2133,7 @@ function form_test_checkboxes_zero($form, &$form_state, $json = TRUE) { '#value' => 'Save', ); if ($json) { - $form['#submit'][] = '_form_test_checkbox_submit'; + $form['#submit'][] = '_form_test_submit_values_json'; } else { $form['#submit'][] = '_form_test_checkboxes_zero_no_redirect'; diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/VocabularyUnitTest.php b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/VocabularyUnitTest.php index 8d94383d3dad6530cca91a418aee8bd70bb3d827..883ab6e0952d5db599d9ccc4487faed45725aaef 100644 --- a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/VocabularyUnitTest.php +++ b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/VocabularyUnitTest.php @@ -206,7 +206,7 @@ function testUninstallReinstall() { module_disable(array('taxonomy')); require_once DRUPAL_ROOT . '/core/includes/install.inc'; - drupal_uninstall_modules(array('taxonomy')); + module_uninstall(array('taxonomy')); module_enable(array('taxonomy')); // Now create a vocabulary with the same name. All field instances diff --git a/core/modules/user/lib/Drupal/user/Tests/UserCreateTest.php b/core/modules/user/lib/Drupal/user/Tests/UserCreateTest.php index cfa725443ec8dbff47fa7b6ed1216fbffae147ee..f72812337f49407cc45dc5d3cbdc2d06fab4f033 100644 --- a/core/modules/user/lib/Drupal/user/Tests/UserCreateTest.php +++ b/core/modules/user/lib/Drupal/user/Tests/UserCreateTest.php @@ -30,9 +30,18 @@ protected function testUserAdd() { $user = $this->drupalCreateUser(array('administer users')); $this->drupalLogin($user); + // Test user creation page for valid fields. + $this->drupalGet('admin/people/create'); + $this->assertFieldbyId('edit-status-0', 0, 'The user status option Blocked exists.', 'User login'); + $this->assertFieldbyId('edit-status-1', 1, 'The user status option Active exists.', 'User login'); + $this->assertFieldByXPath('//input[@type="radio" and @id="edit-status-1" and @checked="checked"]', NULL, 'Default setting for user status is active.'); + + // We create two users, notifying one and not notifying the other, to + // ensure that the tests work in both cases. foreach (array(FALSE, TRUE) as $notify) { + $name = $this->randomName(); $edit = array( - 'name' => $this->randomName(), + 'name' => $name, 'mail' => $this->randomName() . '@example.com', 'pass[pass1]' => $pass = $this->randomString(), 'pass[pass2]' => $pass, @@ -51,6 +60,8 @@ protected function testUserAdd() { $this->drupalGet('admin/people'); $this->assertText($edit['name'], 'User found in list of users'); + $user = user_load_by_name($name); + $this->assertEqual($user->status == 1, 'User is not blocked'); } } } diff --git a/core/modules/user/lib/Drupal/user/User.php b/core/modules/user/lib/Drupal/user/User.php index 992923c8634c99e88d65908a8c28d687a46d451e..382d3d75e4a8c7b0d1b4731ae713b3797d500909 100644 --- a/core/modules/user/lib/Drupal/user/User.php +++ b/core/modules/user/lib/Drupal/user/User.php @@ -91,7 +91,7 @@ class User extends Entity { * * @var integer */ - public $status = 0; + public $status = 1; /** * The user's timezone. diff --git a/core/themes/bartik/css/style.css b/core/themes/bartik/css/style.css index 72f776de4a3dd75c78b57e786baa9897863c235a..7773eda86034913e7dca7275458e9822a53b9b0e 100644 --- a/core/themes/bartik/css/style.css +++ b/core/themes/bartik/css/style.css @@ -1200,6 +1200,7 @@ input.form-email, input.form-url, input.form-search, input.form-number, +input.form-color, textarea.form-textarea, select.form-select { border: 1px solid #ccc; diff --git a/core/themes/bartik/template.php b/core/themes/bartik/template.php index eaeb38dbdb107abdab5c58f66e0a60c8b74fda23..aefb861022a001de029de2e87b8ca104bd4af9c7 100644 --- a/core/themes/bartik/template.php +++ b/core/themes/bartik/template.php @@ -146,7 +146,7 @@ function bartik_field__taxonomy_term_reference($variables) { $output .= '</ul>'; // Render the top-level DIV. - $output = '<div class="' . $variables['classes'] . (!in_array('clearfix', $variables['classes_array']) ? ' clearfix' : '') . '">' . $output . '</div>'; + $output = '<div class="' . $variables['classes'] . (!in_array('clearfix', $variables['classes_array']) ? ' clearfix' : '') . '"' . $variables['attributes'] .'>' . $output . '</div>'; return $output; } diff --git a/core/themes/seven/style.css b/core/themes/seven/style.css index 380aec821faf0f06ee9888bf816a39015fb3c744..ae443a449c0e5e884ae23f1dcb3bf6890d7f2f7d 100644 --- a/core/themes/seven/style.css +++ b/core/themes/seven/style.css @@ -610,6 +610,7 @@ div.teaser-checkbox .form-item, .form-disabled input.form-url, .form-disabled input.form-search, .form-disabled input.form-number, +.form-disabled input.form-color, .form-disabled input.form-file, .form-disabled textarea.form-textarea, .form-disabled select.form-select { @@ -701,6 +702,7 @@ input.form-email, input.form-url, input.form-search, input.form-number, +input.form-color, input.form-file, textarea.form-textarea, select.form-select { @@ -719,6 +721,7 @@ input.form-email:focus, input.form-url:focus, input.form-search:focus, input.form-number:focus, +input.form-color:focus, input.form-file:focus, textarea.form-textarea:focus, select.form-select:focus {