Merge branch '8.x' of git.drupal.org:project/drupal into 8.x
commit
3a9a31251d
|
@ -7025,6 +7025,9 @@ function drupal_common_theme() {
|
|||
'number' => array(
|
||||
'render element' => 'element',
|
||||
),
|
||||
'range' => array(
|
||||
'render element' => 'element',
|
||||
),
|
||||
'form' => array(
|
||||
'render element' => 'element',
|
||||
),
|
||||
|
|
|
@ -606,7 +606,7 @@ function file_load_multiple($fids = array(), $conditions = array()) {
|
|||
* A file ID.
|
||||
*
|
||||
* @return
|
||||
* A file object.
|
||||
* An object representing the file, or FALSE if the file was not found.
|
||||
*
|
||||
* @see hook_file_load()
|
||||
* @see file_load_multiple()
|
||||
|
|
|
@ -3968,6 +3968,29 @@ function theme_number($variables) {
|
|||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns HTML for a range form element.
|
||||
*
|
||||
* @param $variables
|
||||
* An associative array containing:
|
||||
* - element: An associative array containing the properties of the element.
|
||||
* Properties used: #title, #value, #description, #size, #min, #max,
|
||||
* #required, #attributes, #step.
|
||||
*
|
||||
* @ingroup themeable
|
||||
*/
|
||||
function theme_range($variables) {
|
||||
$element = $variables['element'];
|
||||
|
||||
$element['#attributes']['type'] = 'range';
|
||||
element_set_attributes($element, array('id', 'name', 'value', 'step', 'min', 'max'));
|
||||
_form_set_class($element, array('form-range'));
|
||||
|
||||
$output = '<input' . drupal_attributes($element['#attributes']) . ' />';
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Form element validation handler for #type 'number'.
|
||||
*
|
||||
|
|
|
@ -20,20 +20,20 @@ namespace Drupal\Core\Cache;
|
|||
* Drupal\Core\Cache\CacheBackendInterface was called MyCustomCache, the
|
||||
* following line would make Drupal use it for the 'cache_page' bin:
|
||||
* @code
|
||||
* variable_set('cache_class_cache_page', 'MyCustomCache');
|
||||
* $conf['cache_classes']['cache_page'] = 'MyCustomCache';
|
||||
* @endcode
|
||||
*
|
||||
* Additionally, you can register your cache implementation to be used by
|
||||
* default for all cache bins by setting the variable 'cache_default_class' to
|
||||
* the name of your implementation of the
|
||||
* Drupal\Core\Cache\CacheBackendInterface, e.g.
|
||||
* default for all cache bins by setting the $conf['cache_classes'] variable and
|
||||
* changing the value of the 'cache' key to the name of your implementation of
|
||||
* the Drupal\Core\Cache\CacheBackendInterface, e.g.
|
||||
* @code
|
||||
* variable_set('cache_default_class', 'MyCustomCache');
|
||||
* $conf['cache_classes']['cache'] = 'MyCustomCache';
|
||||
* @endcode
|
||||
*
|
||||
* To implement a completely custom cache bin, use the same variable format:
|
||||
* @code
|
||||
* variable_set('cache_class_custom_bin', 'MyCustomCache');
|
||||
* $conf['cache_classes']['custom_bin'] = 'MyCustomCache';
|
||||
* @endcode
|
||||
* To access your custom cache bin, specify the name of the bin when storing
|
||||
* or retrieving cached data:
|
||||
|
@ -104,7 +104,7 @@ interface CacheBackendInterface {
|
|||
* identify objects used to build the cache item, which should trigger
|
||||
* cache invalidation when updated. For example if a cached item represents
|
||||
* a node, both the node ID and the author's user ID might be passed in as
|
||||
* tags. For example array('node' => array(123), 'user' => array(92)).*
|
||||
* tags. For example array('node' => array(123), 'user' => array(92)).
|
||||
*/
|
||||
function set($cid, $data, $expire = CACHE_PERMANENT, array $tags = array());
|
||||
|
||||
|
|
|
@ -959,6 +959,10 @@ function field_has_data($field) {
|
|||
->fieldCondition($field)
|
||||
->range(0, 1)
|
||||
->count()
|
||||
// Neutralize the 'entity_field_access' query tag added by
|
||||
// field_sql_storage_field_storage_query(). The result cannot depend on the
|
||||
// access grants of the current user.
|
||||
->addTag('DANGEROUS_ACCESS_CHECK_OPT_OUT')
|
||||
->execute();
|
||||
}
|
||||
|
||||
|
|
|
@ -515,7 +515,12 @@ function field_sql_storage_field_storage_query(EntityFieldQuery $query) {
|
|||
}
|
||||
else {
|
||||
$select_query = db_select($tablename, $table_alias);
|
||||
$select_query->addTag('entity_field_access');
|
||||
// Allow queries internal to the Field API to opt out of the access
|
||||
// check, for situations where the query's results should not depend on
|
||||
// the access grants for the current user.
|
||||
if (!isset($query->tags['DANGEROUS_ACCESS_CHECK_OPT_OUT'])) {
|
||||
$select_query->addTag('entity_field_access');
|
||||
}
|
||||
$select_query->addMetaData('base_table', $tablename);
|
||||
$select_query->fields($table_alias, array('entity_type', 'entity_id', 'revision_id', 'bundle'));
|
||||
$field_base_table = $table_alias;
|
||||
|
|
|
@ -69,7 +69,7 @@ function help_help($path, $arg) {
|
|||
* Implements hook_preprocess_block().
|
||||
*/
|
||||
function help_preprocess_block(&$variables) {
|
||||
if ($variables['block']->module == 'help') {
|
||||
if ($variables['block']->module == 'system' && $variables['block']->delta == 'help') {
|
||||
$variables['attributes_array']['role'] = 'complementary';
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2510,3 +2510,77 @@ class NodeAccessPagerTestCase extends DrupalWebTestCase {
|
|||
$this->assertNoRaw('page=2', t('No third page exists.'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the interaction of the node access system with fields.
|
||||
*/
|
||||
class NodeAccessFieldTestCase extends NodeWebTestCase {
|
||||
|
||||
public static function getInfo() {
|
||||
return array(
|
||||
'name' => 'Node access and fields',
|
||||
'description' => 'Tests the interaction of the node access system with fields.',
|
||||
'group' => 'Node',
|
||||
);
|
||||
}
|
||||
|
||||
public function setUp() {
|
||||
parent::setUp('node_access_test', 'field_ui');
|
||||
node_access_rebuild();
|
||||
|
||||
// Create some users.
|
||||
$this->admin_user = $this->drupalCreateUser(array('access content', 'bypass node access'));
|
||||
$this->content_admin_user = $this->drupalCreateUser(array('access content', 'administer content types'));
|
||||
|
||||
// Add a custom field to the page content type.
|
||||
$this->field_name = drupal_strtolower($this->randomName() . '_field_name');
|
||||
$this->field = field_create_field(array('field_name' => $this->field_name, 'type' => 'text'));
|
||||
$this->instance = field_create_instance(array(
|
||||
'field_name' => $this->field_name,
|
||||
'entity_type' => 'node',
|
||||
'bundle' => 'page',
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests administering fields when node access is restricted.
|
||||
*/
|
||||
function testNodeAccessAdministerField() {
|
||||
// Create a page node.
|
||||
$langcode = LANGUAGE_NOT_SPECIFIED;
|
||||
$field_data = array();
|
||||
$value = $field_data[$langcode][0]['value'] = $this->randomName();
|
||||
$node = $this->drupalCreateNode(array($this->field_name => $field_data));
|
||||
|
||||
// Log in as the administrator and confirm that the field value is present.
|
||||
$this->drupalLogin($this->admin_user);
|
||||
$this->drupalGet("node/{$node->nid}");
|
||||
$this->assertText($value, 'The saved field value is visible to an administrator.');
|
||||
|
||||
// Log in as the content admin and try to view the node.
|
||||
$this->drupalLogin($this->content_admin_user);
|
||||
$this->drupalGet("node/{$node->nid}");
|
||||
$this->assertText('Access denied', 'Access is denied for the content admin.');
|
||||
|
||||
// Modify the field default as the content admin.
|
||||
$edit = array();
|
||||
$default = 'Sometimes words have two meanings';
|
||||
$edit["{$this->field_name}[$langcode][0][value]"] = $default;
|
||||
$this->drupalPost(
|
||||
"admin/structure/types/manage/page/fields/{$this->field_name}",
|
||||
$edit,
|
||||
t('Save settings')
|
||||
);
|
||||
|
||||
// Log in as the administrator.
|
||||
$this->drupalLogin($this->admin_user);
|
||||
|
||||
// Confirm that the existing node still has the correct field value.
|
||||
$this->drupalGet("node/{$node->nid}");
|
||||
$this->assertText($value, 'The original field value is visible to an administrator.');
|
||||
|
||||
// Confirm that the new default value appears when creating a new node.
|
||||
$this->drupalGet('node/add/page');
|
||||
$this->assertRaw($default, 'The updated default value is displayed when creating a new node.');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
* @see template_preprocess_poll_results()
|
||||
*/
|
||||
?>
|
||||
<article class="poll">
|
||||
<div class="poll">
|
||||
<?php if ($block): ?>
|
||||
<h3 class="poll-title"><?php print $title; ?></h3>
|
||||
<?php endif; ?>
|
||||
|
@ -30,7 +30,7 @@
|
|||
<?php if (!empty($cancel_form)): ?>
|
||||
<?php print $cancel_form; ?>
|
||||
<?php endif; ?>
|
||||
</article>
|
||||
</div>
|
||||
<?php if ($block): ?>
|
||||
<div class="links"><?php print $links; ?></div>
|
||||
<?php endif; ?>
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
* @see template_preprocess_poll_vote()
|
||||
*/
|
||||
?>
|
||||
<article class="poll">
|
||||
<div class="poll">
|
||||
<div class="vote-form">
|
||||
|
||||
<?php if ($block): ?>
|
||||
|
@ -26,4 +26,4 @@
|
|||
</div>
|
||||
<?php // This is the 'rest' of the form, in case items have been added. ?>
|
||||
<?php print $rest ?>
|
||||
</article>
|
||||
</div>
|
||||
|
|
|
@ -233,10 +233,10 @@ class PollCreateTestCase extends PollWebTestCase {
|
|||
$this->clickLink($title);
|
||||
$this->assertText($new_option, 'New option found.');
|
||||
|
||||
$option = $this->xpath('//article[@id="node-1"]//article[@class="poll"]//dt[@class="choice-title"]');
|
||||
$option = $this->xpath('//article[@id="node-1"]//div[@class="poll"]//dt[@class="choice-title"]');
|
||||
$this->assertEqual(end($option), $new_option, 'Last item is equal to new option.');
|
||||
|
||||
$votes = $this->xpath('//article[@id="node-1"]//article[@class="poll"]//div[@class="percent"]');
|
||||
$votes = $this->xpath('//article[@id="node-1"]//div[@class="poll"]//div[@class="percent"]');
|
||||
$this->assertTrue(strpos(end($votes), $vote_count) > 0, t("Votes saved."));
|
||||
}
|
||||
|
||||
|
|
|
@ -2267,6 +2267,7 @@ class DrupalWebTestCase extends DrupalTestCase {
|
|||
case 'textarea':
|
||||
case 'url':
|
||||
case 'number':
|
||||
case 'range':
|
||||
case 'hidden':
|
||||
case 'password':
|
||||
case 'email':
|
||||
|
|
|
@ -413,6 +413,18 @@ function system_element_info() {
|
|||
'#theme' => 'number',
|
||||
'#theme_wrappers' => array('form_element'),
|
||||
);
|
||||
$types['range'] = array(
|
||||
'#input' => TRUE,
|
||||
'#size' => 30,
|
||||
'#step' => 1,
|
||||
'#min' => 0,
|
||||
'#max' => 100,
|
||||
'#maxlength' => 128,
|
||||
'#process' => array('ajax_process_form'),
|
||||
'#element_validate' => array('form_validate_number'),
|
||||
'#theme' => 'range',
|
||||
'#theme_wrappers' => array('form_element'),
|
||||
);
|
||||
$types['machine_name'] = array(
|
||||
'#input' => TRUE,
|
||||
'#default_value' => NULL,
|
||||
|
|
|
@ -301,12 +301,11 @@ class FormsTestCase extends DrupalWebTestCase {
|
|||
}
|
||||
|
||||
/**
|
||||
* Tests validation of #type 'number' elements.
|
||||
* Tests validation of #type 'number' and 'range' elements.
|
||||
*/
|
||||
function testNumber() {
|
||||
$form = $form_state = array();
|
||||
$form = form_test_number($form, $form_state);
|
||||
$this->drupalGet('form-test/number');
|
||||
|
||||
// Array with all the error messages to be checked.
|
||||
$error_messages = array(
|
||||
|
@ -339,25 +338,28 @@ class FormsTestCase extends DrupalWebTestCase {
|
|||
'float_step_any_no_error' => 0,
|
||||
);
|
||||
|
||||
// Post form and show errors.
|
||||
$this->drupalPost(NULL, array(), 'Submit');
|
||||
// First test the number element type, then range.
|
||||
foreach (array('form-test/number', 'form-test/number/range') as $path) {
|
||||
// Post form and show errors.
|
||||
$this->drupalPost($path, array(), 'Submit');
|
||||
|
||||
foreach ($expected as $element => $error) {
|
||||
// Create placeholder array.
|
||||
$placeholders = array(
|
||||
'%name' => $form[$element]['#title'],
|
||||
'%min' => isset($form[$element]['#min']) ? $form[$element]['#min'] : '0',
|
||||
'%max' => isset($form[$element]['#max']) ? $form[$element]['#max'] : '0',
|
||||
);
|
||||
foreach ($expected as $element => $error) {
|
||||
// Create placeholder array.
|
||||
$placeholders = array(
|
||||
'%name' => $form[$element]['#title'],
|
||||
'%min' => isset($form[$element]['#min']) ? $form[$element]['#min'] : '0',
|
||||
'%max' => isset($form[$element]['#max']) ? $form[$element]['#max'] : '0',
|
||||
);
|
||||
|
||||
foreach ($error_messages as $id => $message) {
|
||||
// Check if the error exists on the page, if the current message ID is
|
||||
// expected. Otherwise ensure that the error message is not present.
|
||||
if ($id === $error) {
|
||||
$this->assertRaw(format_string($message, $placeholders));
|
||||
}
|
||||
else {
|
||||
$this->assertNoRaw(format_string($message, $placeholders));
|
||||
foreach ($error_messages as $id => $message) {
|
||||
// Check if the error exists on the page, if the current message ID is
|
||||
// expected. Otherwise ensure that the error message is not present.
|
||||
if ($id === $error) {
|
||||
$this->assertRaw(format_string($message, $placeholders));
|
||||
}
|
||||
else {
|
||||
$this->assertNoRaw(format_string($message, $placeholders));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -406,7 +408,7 @@ class FormsTestCase extends DrupalWebTestCase {
|
|||
|
||||
// All the elements should be marked as disabled, including the ones below
|
||||
// the disabled container.
|
||||
$this->assertEqual(count($disabled_elements), 38, 'The correct elements have the disabled property in the HTML code.');
|
||||
$this->assertEqual(count($disabled_elements), 39, '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);
|
||||
|
|
|
@ -139,6 +139,12 @@ function form_test_menu() {
|
|||
'page arguments' => array('form_test_number'),
|
||||
'access callback' => TRUE,
|
||||
);
|
||||
$items['form-test/number/range'] = array(
|
||||
'title' => 'Range',
|
||||
'page callback' => 'drupal_get_form',
|
||||
'page arguments' => array('form_test_number', 'range'),
|
||||
'access callback' => TRUE,
|
||||
);
|
||||
$items['form-test/checkboxes-radios'] = array(
|
||||
'title' => t('Checkboxes, Radios'),
|
||||
'page callback' => 'drupal_get_form',
|
||||
|
@ -1153,11 +1159,14 @@ function form_test_select_submit($form, &$form_state) {
|
|||
}
|
||||
|
||||
/**
|
||||
* Builds a form to test #type 'number' validation.
|
||||
* Builds a form to test #type 'number' and 'range' validation.
|
||||
*
|
||||
* @param $element
|
||||
* The element type to test. Can be 'number' or 'range'. Defaults to 'number'.
|
||||
*/
|
||||
function form_test_number($form, &$form_state) {
|
||||
function form_test_number($form, &$form_state, $element = 'number') {
|
||||
$base = array(
|
||||
'#type' => 'number',
|
||||
'#type' => $element,
|
||||
);
|
||||
|
||||
$form['integer_no_number'] = $base + array(
|
||||
|
@ -1476,23 +1485,16 @@ function _form_test_disabled_elements($form, &$form_state) {
|
|||
);
|
||||
}
|
||||
|
||||
// Weight.
|
||||
$form['weight'] = array(
|
||||
'#type' => 'weight',
|
||||
'#title' => 'weight',
|
||||
'#default_value' => 10,
|
||||
'#test_hijack_value' => 5,
|
||||
'#disabled' => TRUE,
|
||||
);
|
||||
|
||||
// Number.
|
||||
$form['number'] = array(
|
||||
'#type' => 'number',
|
||||
'#title' => 'number',
|
||||
'#disabled' => TRUE,
|
||||
'#default_value' => 1,
|
||||
'#test_hijack_value' => 2,
|
||||
);
|
||||
// Weight, number, range.
|
||||
foreach (array('weight', 'number', 'range') as $type) {
|
||||
$form[$type] = array(
|
||||
'#type' => $type,
|
||||
'#title' => $type,
|
||||
'#default_value' => 10,
|
||||
'#test_hijack_value' => 5,
|
||||
'#disabled' => TRUE,
|
||||
);
|
||||
}
|
||||
|
||||
// Date.
|
||||
$form['date'] = array(
|
||||
|
|
Loading…
Reference in New Issue