diff --git a/core/includes/bootstrap.inc b/core/includes/bootstrap.inc index ef53eaf..4fcfd78 100644 --- a/core/includes/bootstrap.inc +++ b/core/includes/bootstrap.inc @@ -1995,19 +1995,6 @@ function drupal_hash_base64($data) { } /** - * Gets a salt useful for hardening against SQL injection. - * - * @return - * A salt based on information in settings.php, not in the database. - */ -function drupal_get_hash_salt() { - global $drupal_hash_salt; - // If the $drupal_hash_salt variable is empty, a hash of the serialized - // database credentials is used as a fallback salt. - return empty($drupal_hash_salt) ? hash('sha256', serialize(Database::getConnectionInfo('default'))) : $drupal_hash_salt; -} - -/** * Merges multiple arrays, recursively, and returns the merged array. * * This function is similar to PHP's array_merge_recursive() function, but it @@ -2537,6 +2524,7 @@ function typed_data() { * HMAC and timestamp. */ function drupal_valid_test_ua($new_prefix = NULL) { + global $drupal_hash_salt; static $test_prefix; if (isset($new_prefix)) { @@ -2552,7 +2540,7 @@ function drupal_valid_test_ua($new_prefix = NULL) { // We use the salt from settings.php to make the HMAC key, since // the database is not yet initialized and we can't access any Drupal variables. // The file properties add more entropy not easily accessible to others. - $key = drupal_get_hash_salt() . filectime(__FILE__) . fileinode(__FILE__); + $key = $drupal_hash_salt . filectime(__FILE__) . fileinode(__FILE__); $time_diff = REQUEST_TIME - $time; // Since we are making a local request a 5 second time window is allowed, // and the HMAC must match. @@ -2570,13 +2558,14 @@ function drupal_valid_test_ua($new_prefix = NULL) { * Generates a user agent string with a HMAC and timestamp for simpletest. */ function drupal_generate_test_ua($prefix) { + global $drupal_hash_salt; static $key; if (!isset($key)) { // We use the salt from settings.php to make the HMAC key, since // the database is not yet initialized and we can't access any Drupal variables. // The file properties add more entropy not easily accessible to others. - $key = drupal_get_hash_salt() . filectime(__FILE__) . fileinode(__FILE__); + $key = $drupal_hash_salt . filectime(__FILE__) . fileinode(__FILE__); } // Generate a moderately secure HMAC based on the database credentials. $salt = uniqid('', TRUE); @@ -3112,7 +3101,7 @@ function drupal_classloader() { case 'apc': if (function_exists('apc_store')) { require_once DRUPAL_ROOT . '/core/vendor/symfony/class-loader/Symfony/Component/ClassLoader/ApcUniversalClassLoader.php'; - $loader = new ApcUniversalClassLoader('drupal.' . drupal_get_hash_salt()); + $loader = new ApcUniversalClassLoader('drupal.' . $GLOBALS['drupal_hash_salt']); break; } // Fall through to the default loader if APC was not loaded, so that the @@ -3477,7 +3466,7 @@ function drupal_php_storage($name = 'default') { else { $configuration = array( 'class' => 'Drupal\Component\PhpStorage\MTimeProtectedFileStorage', - 'secret' => drupal_get_hash_salt(), + 'secret' => $GLOBALS['drupal_hash_salt'], ); } $class = isset($configuration['class']) ? $configuration['class'] : 'Drupal\Component\PhpStorage\MTimeProtectedFileStorage'; diff --git a/core/includes/common.inc b/core/includes/common.inc index f8a54e9..bc05a2b 100644 --- a/core/includes/common.inc +++ b/core/includes/common.inc @@ -4825,6 +4825,19 @@ function drupal_json_decode($var) { } /** + * Gets a salt useful for hardening against SQL injection. + * + * @return + * A salt based on information in settings.php, not in the database. + */ +function drupal_get_hash_salt() { + global $drupal_hash_salt, $databases; + // If the $drupal_hash_salt variable is empty, a hash of the serialized + // database credentials is used as a fallback salt. + return empty($drupal_hash_salt) ? hash('sha256', serialize($databases)) : $drupal_hash_salt; +} + +/** * Ensures the private key variable used to generate tokens is set. * * @return diff --git a/core/modules/path/lib/Drupal/path/Tests/PathAliasTest.php b/core/modules/path/lib/Drupal/path/Tests/PathAliasTest.php index 181bd72..5fce9e5 100644 --- a/core/modules/path/lib/Drupal/path/Tests/PathAliasTest.php +++ b/core/modules/path/lib/Drupal/path/Tests/PathAliasTest.php @@ -12,13 +12,6 @@ */ class PathAliasTest extends PathTestBase { - /** - * Modules to enable. - * - * @var array - */ - public static $modules = array('path'); - public static function getInfo() { return array( 'name' => 'Path alias functionality', @@ -142,23 +135,23 @@ function testNodeAlias() { // Create alias. $edit = array(); - $edit['path[alias]'] = $this->randomName(8); + $edit['path[und][0][value]'] = $this->randomName(8); $this->drupalPost('node/' . $node1->nid . '/edit', $edit, t('Save')); // Confirm that the alias works. - $this->drupalGet($edit['path[alias]']); + $this->drupalGet($edit['path[und][0][value]']); $this->assertText($node1->label(), 'Alias works.'); $this->assertResponse(200); // Change alias to one containing "exotic" characters. - $previous = $edit['path[alias]']; - $edit['path[alias]'] = "- ._~!$'\"()*@[]?&+%#,;=:" . // "Special" ASCII characters. + $previous = $edit['path[und][0][value]']; + $edit['path[und][0][value]'] = "- ._~!$'\"()*@[]?&+%#,;=:" . // "Special" ASCII characters. "%23%25%26%2B%2F%3F" . // Characters that look like a percent-escaped string. "éøïвβ中國書۞"; // Characters from various non-ASCII alphabets. $this->drupalPost('node/' . $node1->nid . '/edit', $edit, t('Save')); // Confirm that the alias works. - $this->drupalGet($edit['path[alias]']); + $this->drupalGet($edit['path[und][0][value]']); $this->assertText($node1->label(), 'Changed alias works.'); $this->assertResponse(200); @@ -171,17 +164,17 @@ function testNodeAlias() { $node2 = $this->drupalCreateNode(); // Set alias to second test node. - // Leave $edit['path[alias]'] the same. + // Leave $edit['path[und][0][value]'] the same. $this->drupalPost('node/' . $node2->nid . '/edit', $edit, t('Save')); // Confirm that the alias didn't make a duplicate. $this->assertText(t('The alias is already in use.'), 'Attempt to moved alias was rejected.'); // Delete alias. - $this->drupalPost('node/' . $node1->nid . '/edit', array('path[alias]' => ''), t('Save')); + $this->drupalPost('node/' . $node1->nid . '/edit', array('path[und][0][value]' => ''), t('Save')); // Confirm that the alias no longer works. - $this->drupalGet($edit['path[alias]']); + $this->drupalGet($edit['path[und][0][value]']); $this->assertNoText($node1->label(), 'Alias was successfully deleted.'); $this->assertResponse(404); } @@ -206,13 +199,13 @@ function testDuplicateNodeAlias() { // Create one node with a random alias. $node_one = $this->drupalCreateNode(); $edit = array(); - $edit['path[alias]'] = $this->randomName(); + $edit['path[und][0][value]'] = $this->randomName(); $this->drupalPost('node/' . $node_one->nid . '/edit', $edit, t('Save')); // Now create another node and try to set the same alias. $node_two = $this->drupalCreateNode(); $this->drupalPost('node/' . $node_two->nid . '/edit', $edit, t('Save')); $this->assertText(t('The alias is already in use.')); - $this->assertFieldByXPath("//input[@name='path[alias]' and contains(@class, 'error')]", $edit['path[alias]'], 'Textfield exists and has the error class.'); + $this->assertFieldByXPath("//input[@name='path[und][0][value]' and contains(@class, 'error')]", $edit['path[und][0][value]'], 'Textfield exists and has the error class.'); } } diff --git a/core/modules/path/lib/Drupal/path/Tests/PathFieldCRUDTest.php b/core/modules/path/lib/Drupal/path/Tests/PathFieldCRUDTest.php index dc74d8e..97660ae 100644 --- a/core/modules/path/lib/Drupal/path/Tests/PathFieldCRUDTest.php +++ b/core/modules/path/lib/Drupal/path/Tests/PathFieldCRUDTest.php @@ -36,8 +36,8 @@ function setUp() { $this->installSchema('system', 'url_alias'); $this->nodeType = (object) array( - 'type' => 'article', - 'name' => 'Article', + 'type' => 'page', + 'name' => 'Basic page', ); node_type_save($this->nodeType); diff --git a/core/modules/path/lib/Drupal/path/Tests/PathLanguageTest.php b/core/modules/path/lib/Drupal/path/Tests/PathLanguageTest.php index e6fd10d..4338bee 100644 --- a/core/modules/path/lib/Drupal/path/Tests/PathLanguageTest.php +++ b/core/modules/path/lib/Drupal/path/Tests/PathLanguageTest.php @@ -17,7 +17,7 @@ class PathLanguageTest extends PathTestBase { * * @var array */ - public static $modules = array('path', 'locale', 'translation'); + public static $modules = array('locale', 'translation_entity'); public static function getInfo() { return array( @@ -31,18 +31,26 @@ function setUp() { parent::setUp(); // Create and login user. - $this->web_user = $this->drupalCreateUser(array('edit any page content', 'create page content', 'administer url aliases', 'create url aliases', 'administer languages', 'translate all content', 'access administration pages', 'administer content types')); + $this->web_user = $this->drupalCreateUser(array('edit any page content', 'create page content', 'administer url aliases', 'create url aliases', 'administer languages', 'translate any entity', 'access administration pages', 'administer content types')); $this->drupalLogin($this->web_user); // Enable French language. $edit = array(); $edit['predefined_langcode'] = 'fr'; - $this->drupalPost('admin/config/regional/language/add', $edit, t('Add language')); // Enable URL language detection and selection. $edit = array('language_interface[enabled][language-url]' => 1); $this->drupalPost('admin/config/regional/language/detection', $edit, t('Save settings')); + + // Enable entity/field translation for the body field. + $field = field_info_field('body'); + $field['translatable'] = TRUE; + field_update_field($field); + // Enable entity/field translation for the default path field. + $field = field_info_field('path'); + $field['translatable'] = TRUE; + field_update_field($field); } /** @@ -52,45 +60,44 @@ function testAliasTranslation() { // Set 'page' content type to enable translation. $edit = array( 'language_configuration[language_hidden]' => FALSE, + 'language_configuration[translation_entity]' => TRUE, ); $this->drupalPost('admin/structure/types/manage/page', $edit, t('Save content type')); $this->assertRaw(t('The content type %type has been updated.', array('%type' => 'Basic page')), 'Basic page content type has been updated.'); - variable_set('node_type_language_translation_enabled_page', TRUE); - $english_node = $this->drupalCreateNode(array('type' => 'page')); + $node = $this->drupalCreateNode(array('type' => 'page')); $english_alias = $this->randomName(); // Edit the node to set language and path. $edit = array(); $edit['langcode'] = 'en'; - $edit['path[alias]'] = $english_alias; - $this->drupalPost('node/' . $english_node->nid . '/edit', $edit, t('Save')); + $edit['path[und][0][value]'] = $english_alias; + $this->drupalPost('node/' . $node->nid . '/edit', $edit, t('Save')); // Confirm that the alias works. $this->drupalGet($english_alias); - $this->assertText($english_node->label(), 'Alias works.'); + $this->assertText($node->label(), 'Alias works.'); // Translate the node into French. - $this->drupalGet('node/' . $english_node->nid . '/translate'); - $this->clickLink(t('add translation')); + $this->drupalGet('node/' . $node->nid . '/translations'); + $this->clickLink(t('add')); $edit = array(); - $langcode = LANGUAGE_NOT_SPECIFIED; - $edit["title"] = $this->randomName(); - $edit["body[$langcode][0][value]"] = $this->randomName(); + $edit['title'] = $this->randomName(); + $edit['body[fr][0][value]'] = $this->randomName(); $french_alias = $this->randomName(); - $edit['path[alias]'] = $french_alias; + $edit['path[fr][0][value]'] = $french_alias; $this->drupalPost(NULL, $edit, t('Save')); // Clear the path lookup cache. drupal_lookup_path('wipe'); // Ensure the node was created. - $french_node = $this->drupalGetNodeByTitle($edit["title"]); - $this->assertTrue(($french_node), 'Node found in database.'); + $node = $this->drupalGetNodeByTitle($edit["title"]); + $this->assertTrue($node, 'Node found in database.'); // Confirm that the alias works. - $this->drupalGet('fr/' . $edit['path[alias]']); - $this->assertText($french_node->label(), 'Alias for French translation works.'); + $this->drupalGet('fr/' . $edit['path[fr][0][value]']); + $this->assertText($node->label(), 'Alias for French translation works.'); // Confirm that the alias is returned by url(). Languages are cached on // many levels, and we need to clear those caches. @@ -98,8 +105,8 @@ function testAliasTranslation() { drupal_static_reset('language_url_outbound_alter'); drupal_static_reset('language_url_rewrite_url'); $languages = language_list(); - $url = url('node/' . $french_node->nid, array('language' => $languages[$french_node->langcode])); - $this->assertTrue(strpos($url, $edit['path[alias]']), 'URL contains the path alias.'); + $url = url('node/' . $node->nid, array('language' => language_load('fr'))); + $this->assertTrue(strpos($url, $edit['path[fr][0][value]']), 'URL contains the path alias.'); // Confirm that the alias works even when changing language negotiation // options. Enable User language detection and selection over URL one. @@ -124,11 +131,11 @@ function testAliasTranslation() { // path alias for French matching the english alias. So drupal_lookup_path() // needs to use the URL language to check whether the alias is valid. $this->drupalGet($english_alias); - $this->assertText($english_node->label(), 'Alias for English translation works.'); + $this->assertText($node->body['en'][0]['value'], 'Alias for English translation works.'); // Check that the French alias works. $this->drupalGet("fr/$french_alias"); - $this->assertText($french_node->label(), 'Alias for French translation works.'); + $this->assertText($node->body['fr'][0]['value'], 'Alias for French translation works.'); // Disable URL language negotiation. $edit = array('language_interface[enabled][language-url]' => FALSE); @@ -136,7 +143,7 @@ function testAliasTranslation() { // Check that the English alias still works. $this->drupalGet($english_alias); - $this->assertText($english_node->label(), 'Alias for English translation works.'); + $this->assertText($node->body['en'][0]['value'], 'Alias for English translation works.'); // Check that the French alias is not available. We check the unprefixed // alias because we disabled URL language negotiation above. In this @@ -148,17 +155,17 @@ function testAliasTranslation() { // drupal_lookup_path() has an internal static cache. Check to see that // it has the appropriate contents at this point. drupal_lookup_path('wipe'); - $french_node_path = drupal_lookup_path('source', $french_alias, $french_node->langcode); - $this->assertEqual($french_node_path, 'node/' . $french_node->nid, 'Normal path works.'); + $french_node_path = drupal_lookup_path('source', $french_alias, 'fr'); + $this->assertEqual($french_node_path, 'node/' . $node->nid, 'Normal path works.'); // Second call should return the same path. - $french_node_path = drupal_lookup_path('source', $french_alias, $french_node->langcode); - $this->assertEqual($french_node_path, 'node/' . $french_node->nid, 'Normal path is the same.'); + $french_node_path = drupal_lookup_path('source', $french_alias, 'fr'); + $this->assertEqual($french_node_path, 'node/' . $node->nid, 'Normal path is the same.'); // Confirm that the alias works. - $french_node_alias = drupal_lookup_path('alias', 'node/' . $french_node->nid, $french_node->langcode); + $french_node_alias = drupal_lookup_path('alias', 'node/' . $node->nid, 'fr'); $this->assertEqual($french_node_alias, $french_alias, 'Alias works.'); // Second call should return the same alias. - $french_node_alias = drupal_lookup_path('alias', 'node/' . $french_node->nid, $french_node->langcode); + $french_node_alias = drupal_lookup_path('alias', 'node/' . $node->nid, 'fr'); $this->assertEqual($french_node_alias, $french_alias, 'Alias is the same.'); } } diff --git a/core/modules/path/lib/Drupal/path/Tests/PathLanguageUiTest.php b/core/modules/path/lib/Drupal/path/Tests/PathLanguageUiTest.php index bd0d279..ab6a9bd 100644 --- a/core/modules/path/lib/Drupal/path/Tests/PathLanguageUiTest.php +++ b/core/modules/path/lib/Drupal/path/Tests/PathLanguageUiTest.php @@ -17,7 +17,7 @@ class PathLanguageUiTest extends PathTestBase { * * @var array */ - public static $modules = array('path', 'locale'); + public static $modules = array('locale'); public static function getInfo() { return array( diff --git a/core/modules/path/lib/Drupal/path/Tests/PathTaxonomyTermTest.php b/core/modules/path/lib/Drupal/path/Tests/PathTaxonomyTermTest.php index 421870d..25bc7b2 100644 --- a/core/modules/path/lib/Drupal/path/Tests/PathTaxonomyTermTest.php +++ b/core/modules/path/lib/Drupal/path/Tests/PathTaxonomyTermTest.php @@ -30,12 +30,13 @@ public static function getInfo() { function setUp() { parent::setUp(); - // Create a Tags vocabulary for the Article node type. + // Create a Tags vocabulary for the Basic page node type. $vocabulary = entity_create('taxonomy_vocabulary', array( 'name' => t('Tags'), 'machine_name' => 'tags', )); $vocabulary->save(); + path_add_default_field_instance('taxonomy_term', 'tags'); // Create and login user. $web_user = $this->drupalCreateUser(array('administer url aliases', 'administer taxonomy', 'access administration pages')); @@ -52,35 +53,35 @@ function testTermAlias() { $edit = array(); $edit['name'] = $this->randomName(); $edit['description[value]'] = $description; - $edit['path[alias]'] = $this->randomName(); + $edit['path[und][0][value]'] = $this->randomName(); $this->drupalPost('admin/structure/taxonomy/' . $vocabulary->machine_name . '/add', $edit, t('Save')); // Confirm that the alias works. - $this->drupalGet($edit['path[alias]']); + $this->drupalGet($edit['path[und][0][value]']); $this->assertText($description, 'Term can be accessed on URL alias.'); // Change the term's URL alias. $tid = db_query("SELECT tid FROM {taxonomy_term_data} WHERE name = :name", array(':name' => $edit['name']))->fetchField(); $edit2 = array(); - $edit2['path[alias]'] = $this->randomName(); + $edit2['path[und][0][value]'] = $this->randomName(); $this->drupalPost('taxonomy/term/' . $tid . '/edit', $edit2, t('Save')); // Confirm that the changed alias works. - $this->drupalGet($edit2['path[alias]']); + $this->drupalGet($edit2['path[und][0][value]']); $this->assertText($description, 'Term can be accessed on changed URL alias.'); // Confirm that the old alias no longer works. - $this->drupalGet($edit['path[alias]']); + $this->drupalGet($edit['path[und][0][value]']); $this->assertNoText($description, 'Old URL alias has been removed after altering.'); $this->assertResponse(404, 'Old URL alias returns 404.'); // Remove the term's URL alias. $edit3 = array(); - $edit3['path[alias]'] = ''; + $edit3['path[und][0][value]'] = ''; $this->drupalPost('taxonomy/term/' . $tid . '/edit', $edit3, t('Save')); // Confirm that the alias no longer works. - $this->drupalGet($edit2['path[alias]']); + $this->drupalGet($edit2['path[und][0][value]']); $this->assertNoText($description, 'Old URL alias has been removed after altering.'); $this->assertResponse(404, 'Old URL alias returns 404.'); } diff --git a/core/modules/path/lib/Drupal/path/Tests/PathTestBase.php b/core/modules/path/lib/Drupal/path/Tests/PathTestBase.php index 97f3f81..9aafb56 100644 --- a/core/modules/path/lib/Drupal/path/Tests/PathTestBase.php +++ b/core/modules/path/lib/Drupal/path/Tests/PathTestBase.php @@ -24,10 +24,10 @@ function setUp() { parent::setUp(); - // Create Basic page and Article node types. + // Create Basic page node type. if ($this->profile != 'standard') { $this->drupalCreateContentType(array('type' => 'page', 'name' => 'Basic page')); - $this->drupalCreateContentType(array('type' => 'article', 'name' => 'Article')); + path_add_default_field_instance('node', 'page'); } } } diff --git a/core/modules/path/path.install b/core/modules/path/path.install index df0d312..a186424 100644 --- a/core/modules/path/path.install +++ b/core/modules/path/path.install @@ -37,3 +37,11 @@ function path_field_schema($field) { ), ); } + +/** + * Implements hook_install(). + */ +function path_install() { + // Create a default 'path' field that can be attached to any entity bundle. + path_add_default_field(); +} diff --git a/core/modules/path/path.module b/core/modules/path/path.module index a738f60..7173330 100644 --- a/core/modules/path/path.module +++ b/core/modules/path/path.module @@ -52,7 +52,7 @@ function path_field_info() { * Implements hook_field_is_empty(). */ function path_field_is_empty($item, $field) { - return !isset($item['value']) || $item['value'] === ''; + return empty($item['pid']) && (!isset($item['value']) || $item['value'] === ''); } /** @@ -79,11 +79,13 @@ function path_field_update($entity_type, $entity, $field, $instance, $langcode, foreach ($items as &$item) { $item['value'] = trim($item['value']); // Delete old alias if it was erased. - if (!empty($item['pid']) && empty($item['value'])) { - path_delete($item['pid']); - $item['pid'] = NULL; + if ($item['value'] === '') { + if (!empty($item['pid'])) { + path_delete($item['pid']); + $item['pid'] = NULL; + } } - if (!empty($item['value'])) { + else { $uri = $entity->uri(); $item['source'] = $uri['path']; $item['langcode'] = $langcode; @@ -106,6 +108,56 @@ function path_field_delete($entity_type, $entity, $field, $instance, $langcode, } /** + * Creates a default 'path' field that can be attached to any entity bundle. + * + * @return array + * The field information of the default path field. + * + * @see path_add_default_field_instance() + * @see path_install() + */ +function path_add_default_field() { + $field = field_info_field('path'); + if (empty($field)) { + $field = array( + 'field_name' => 'path', + 'type' => 'path', + ); + $field = field_create_field($field); + } + return $field; +} + +/** + * Adds the default path field to an entity bundle. + * + * @param string $entity_type + * The name of the entity type to add the field to. + * @param string $bundle + * The name of the bundle to add the field to. + * + * @return array + * The path field instance. + */ +function path_add_default_field_instance($entity_type, $bundle, $label = 'Permalink') { + $field = path_add_default_field(); + $instance = field_info_instance($entity_type, $field['field_name'], $bundle); + if (empty($instance)) { + $instance = array( + 'field_name' => $field['field_name'], + 'entity_type' => $entity_type, + 'bundle' => $bundle, + 'label' => $label, + 'widget' => array( + 'type' => 'path_default', + ), + ); + $instance = field_create_instance($instance); + } + return $instance; +} + +/** * Implements hook_permission(). */ function path_permission() { @@ -190,6 +242,10 @@ function path_path_delete($path) { } foreach ($entity_types as $entity_type => $field_info) { $query = entity_query($entity_type); + // Disable entity access checks, since field values need to be updated + // regardless of the permissions of the current user (if any). + $query->accessCheck(FALSE); + $group = $query->orConditionGroup(); foreach ($field_info as $field_name => $bundles) { $group->condition($field_name . '.pid', $path['pid']);