Commit 88db3ee7 authored by Dave Reid's avatar Dave Reid Committed by Dave Reid
Browse files

Issue #936222 by mvc, quicksketch, joelpittet, Dave Reid, sammarks15, brianV,...

Issue #936222 by mvc, quicksketch, joelpittet, Dave Reid, sammarks15, brianV, RoloDMonkey, bvanmeurs, effulgentsia, jucallme, ponies, Island Usurper, Pancho, iamEAP, pinoniq, bibishani, Gábor Hojtsy, emanaton, populist, mrmikedewolf, klonos, dsnopek, Fabianx, MiroslavBanov, JamesOakley, acbramley, jstoller, DamienMcKenna, mattlt, rv0: Merged in pathauto_persist module functionality to prevent losing manual aliases when calling node_save().
parent a3d0d2df
......@@ -7,6 +7,40 @@
* @ingroup pathauto
*/
/**
* Implements hook_schema().
*/
function pathauto_schema() {
$schema['pathauto_state'] = array(
'description' => 'The status of each entity alias (whether it was automatically generated or not).',
'fields' => array(
'entity_type' => array(
'type' => 'varchar',
'length' => 32,
'not null' => TRUE,
'description' => 'An entity type.',
),
'entity_id' => array(
'type' => 'int',
'unsigned' => TRUE,
'not null' => TRUE,
'description' => 'An entity ID.',
),
'pathauto' => array(
'type' => 'int',
'size' => 'tiny',
'not null' => TRUE,
'default' => 0,
'description' => 'The automatic alias status of the entity.',
),
),
'primary key' => array('entity_type', 'entity_id'),
);
return $schema;
}
/**
* Implements hook_install().
*/
......@@ -44,6 +78,23 @@ function pathauto_uninstall() {
cache_clear_all('variables', 'cache');
}
/**
* Implements hook_requirements().
*/
function pathauto_requirements($phase) {
$requirements = array();
$t = get_t();
if ($phase == 'runtime' && module_exists('pathauto_persist')) {
$requirements['pathauto'] = array(
'title' => $t('Pathauto Persist'),
'value' => $t('Enabled'),
'description' => $t('Pathauto Persist is installed and enabled. As Pathauto Persist has been merged into Pathauto, the Pathauto Persist module can be safely disabled and removed. All Pathauto Persist settings have been migrated to the Pathauto implementation.'),
'severity' => REQUIREMENT_INFO,
);
}
return $requirements;
}
/**
* Remove the unsupported user/%/contact and user/%/tracker pattern variables.
*/
......@@ -174,6 +225,55 @@ function pathauto_update_7005() {
return 'Your Pathauto taxonomy and forum patterns have been corrected. You may wish to regenerate your taxonomy and forum term URL aliases.';
}
/**
* Create pathauto_state table, using data from pathauto_persist if it exists.
*/
function pathauto_update_7006() {
if (!db_table_exists('pathauto_state')) {
$schema['pathauto_state'] = array(
'description' => 'The status of each entity alias (whether it was automatically generated or not).',
'fields' => array(
'entity_type' => array(
'type' => 'varchar',
'length' => 32,
'not null' => TRUE,
'description' => 'The entity type.',
),
'entity_id' => array(
'type' => 'int',
'unsigned' => TRUE,
'not null' => TRUE,
'description' => 'The entity ID.',
),
'pathauto' => array(
'type' => 'int',
'size' => 'tiny',
'not null' => TRUE,
'default' => 0,
'description' => 'The automatic alias status of the entity.',
),
),
'primary key' => array('entity_type', 'entity_id'),
);
if (db_table_exists('pathauto_persist')) {
// Rename pathauto_persist's table, then create a new empty one just so
// that we can cleanly disable that module.
db_rename_table('pathauto_persist', 'pathauto_state');
db_create_table('pathauto_persist', $schema['pathauto_state']);
// Disable the module and inform the user.
if (module_exists('pathauto_persist')) {
module_disable(array('pathauto_persist'));
}
return t('The Pathauto Persist module and all of its data has been merged into Pathauto. The Pathauto Persist module has been disabled and can be safely uninstalled.');
}
else {
db_create_table('pathauto_state', $schema['pathauto_state']);
}
}
}
/**
* Build a list of Drupal 6 tokens and their Drupal 7 token names.
*/
......
......@@ -338,10 +338,54 @@ function pathauto_field_attach_form($entity_type, $entity, &$form, &$form_state,
}
}
/**
* Implements hook_entity_load().
*/
function pathauto_entity_load($entities, $entity_type) {
// Statically cache which entity types have data in the pathauto_state
// table to avoid unnecessary queries for entities that would not have any
// data anyway.
static $loadable_types;
if (!isset($loadable_types)) {
$loadable_types = &drupal_static(__FUNCTION__);
if (!isset($loadable_types)) {
// Prevent errors if pathauto_update_7006() has not yet been run.
if (!db_table_exists('pathauto_state')) {
$loadable_types = array();
}
else {
$loadable_types = db_query("SELECT DISTINCT entity_type FROM {pathauto_state}")->fetchCol();
}
}
}
// Check if this entity type has loadable records.
if (!in_array($entity_type, $loadable_types)) {
return;
}
$states = pathauto_entity_state_load_multiple($entity_type, array_keys($entities));
foreach ($states as $id => $state) {
if (!isset($entities[$id]->path)) {
$entities[$id]->path = array();
}
if (is_array($entities[$id]->path) && !isset($entities[$id]->path['pathauto'])) {
$entities[$id]->path['pathauto'] = $state;
}
}
}
/**
* Implements hook_entity_presave().
*/
function pathauto_entity_presave($entity, $type) {
function pathauto_entity_presave($entity, $entity_type) {
if (isset($entity->path['pathauto']) && is_array($entity->path)) {
// We must set an empty alias string for the path to prevent saving an
// alias.
$entity->path += array('alias' => '');
}
// About to be saved (before insert/update)
if (!empty($entity->path['pathauto']) && isset($entity->path['old_alias'])
&& $entity->path['alias'] == '' && $entity->path['old_alias'] != '') {
......@@ -363,6 +407,109 @@ function pathauto_entity_presave($entity, $type) {
}
}
/**
* Implements hook_entity_insert().
*/
function pathauto_entity_insert($entity, $entity_type) {
if (isset($entity->path['pathauto'])) {
pathauto_entity_state_save($entity_type, $entity, $entity->path['pathauto']);
}
}
/**
* Implements hook_entity_update().
*/
function pathauto_entity_update($entity, $entity_type) {
if (isset($entity->path['pathauto'])) {
pathauto_entity_state_save($entity_type, $entity, $entity->path['pathauto']);
}
}
/**
* Implements hook_entity_delete().
*/
function pathauto_entity_delete($entity, $entity_type) {
if (isset($entity->path['pathauto'])) {
pathauto_entity_state_delete($entity_type, $entity);
}
}
/**
* Load a pathauto state for an entity.
*
* @param string $entity_type
* An entity type.
* @param int $entity_id
* An entity ID.
*
* @return bool
* A value that evaluates to TRUE if Pathauto should control this entity's
* path. A value that evaluates to FALSE if Pathauto should not manage the
* entity's path.
*/
function pathauto_entity_state_load($entity_type, $entity_id) {
$pathauto_state = pathauto_entity_state_load_multiple($entity_type, array($entity_id));
return !empty($pathauto_state) ? reset($pathauto_state) : FALSE;
}
/**
* Load a pathauto state for multiple entities.
*
* @param string $entity_type
* The entity type.
* @param int[] $entity_ids
* The array of entity IDs.
*
* @return bool[]
* An array of Pathauto states keyed by entity ID.
*/
function pathauto_entity_state_load_multiple($entity_type, $entity_ids) {
return db_query("SELECT entity_id, pathauto FROM {pathauto_state} WHERE entity_type = :entity_type AND entity_id IN (:entity_ids)", array(':entity_type' => $entity_type, ':entity_ids' => $entity_ids))->fetchAllKeyed();
}
/**
* Save the pathauto state for an entity.
*
* @param string $entity_type
* The entity type.
* @param object $entity
* The entity object.
* @param bool $pathauto_state
* A value that evaluates to TRUE means that Pathauto should keep controlling
* this entity's path in the future. A value that evaluates to FALSE means
* that Pathauto should not manage the entity's path.
*/
function pathauto_entity_state_save($entity_type, $entity, $pathauto_state) {
list($entity_id) = entity_extract_ids($entity_type, $entity);
db_merge('pathauto_state')
->key(array(
'entity_type' => $entity_type,
'entity_id' => $entity_id,
))
->fields(array(
'pathauto' => $pathauto_state ? 1 : 0,
))
->execute();
drupal_static_reset('pathauto_entity_load');
}
/**
* Delete the pathauto state for an entity.
*
* @param string $entity_type
* The entity type.
* @param object $entity
* The entity object.
*/
function pathauto_entity_state_delete($entity_type, $entity) {
list($entity_id) = entity_extract_ids($entity_type, $entity);
db_delete('pathauto_state')
->condition('entity_type', $entity_type)
->condition('entity_id', $entity_id)
->execute();
drupal_static_reset('pathauto_entity_load');
}
/**
* Implements hook_action_info().
*/
......
......@@ -55,9 +55,13 @@ class PathautoTestHelper extends DrupalWebTestCase {
$this->assertEntityAlias($entity_type, $entity, $uri['path'], $language);
}
function assertNoEntityAliasExists($entity_type, $entity) {
function assertNoEntityAliasExists($entity_type, $entity, $alias = NULL) {
$uri = entity_uri($entity_type, $entity);
$this->assertNoAliasExists(array('source' => $uri['path']));
$path = array('source' => $uri['path']);
if (!empty($alias)) {
$path['alias'] = $alias;
}
$this->assertNoAliasExists($path);
}
function assertAlias($source, $expected_alias, $language = LANGUAGE_NONE) {
......@@ -565,6 +569,83 @@ class PathautoFunctionalTestCase extends PathautoFunctionalTestHelper {
$this->assertEntityAlias('node', $node2, 'node/' . $node2->nid);
}
/**
* @todo Merge this with existing node test methods?
*/
public function testNodeState() {
$nodeNoAliasUser = $this->drupalCreateUser(array('bypass node access'));
$nodeAliasUser = $this->drupalCreateUser(array('bypass node access', 'create url aliases'));
$node = $this->drupalCreateNode(array(
'title' => 'Node version one',
'type' => 'page',
'path' => array(
'pathauto' => FALSE,
),
));
$this->assertNoEntityAlias('node', $node);
// Set a manual path alias for the node.
$node->path['alias'] = 'test-alias';
node_save($node);
// Ensure that the pathauto field was saved to the database.
$node = node_load($node->nid, NULL, TRUE);
$this->assertFalse($node->path['pathauto']);
// Ensure that the manual path alias was saved and an automatic alias was not generated.
$this->assertEntityAlias('node', $node, 'test-alias');
$this->assertNoEntityAliasExists('node', $node, 'content/node-version-one');
// Save the node as a user who does not have access to path fieldset.
$this->drupalLogin($nodeNoAliasUser);
$this->drupalGet('node/' . $node->nid . '/edit');
$this->assertNoFieldByName('path[pathauto]');
$edit = array('title' => 'Node version two');
$this->drupalPost(NULL, $edit, 'Save');
$this->assertText('Basic page Node version two has been updated.');
$this->assertEntityAlias('node', $node, 'test-alias');
$this->assertNoEntityAliasExists('node', $node, 'content/node-version-one');
$this->assertNoEntityAliasExists('node', $node, 'content/node-version-two');
// Load the edit node page and check that the Pathauto checkbox is unchecked.
$this->drupalLogin($nodeAliasUser);
$this->drupalGet('node/' . $node->nid . '/edit');
$this->assertNoFieldChecked('edit-path-pathauto');
// Edit the manual alias and save the node.
$edit = array(
'title' => 'Node version three',
'path[alias]' => 'manually-edited-alias',
);
$this->drupalPost(NULL, $edit, 'Save');
$this->assertText('Basic page Node version three has been updated.');
$this->assertEntityAlias('node', $node, 'manually-edited-alias');
$this->assertNoEntityAliasExists('node', $node, 'test-alias');
$this->assertNoEntityAliasExists('node', $node, 'content/node-version-one');
$this->assertNoEntityAliasExists('node', $node, 'content/node-version-two');
$this->assertNoEntityAliasExists('node', $node, 'content/node-version-three');
// Programatically save the node with an automatic alias.
$node = node_load($node->nid, NULL, TRUE);
$node->path['pathauto'] = TRUE;
node_save($node);
// Ensure that the pathauto field was saved to the database.
$node = node_load($node->nid, NULL, TRUE);
$this->assertTrue($node->path['pathauto']);
$this->assertEntityAlias('node', $node, 'content/node-version-three');
$this->assertNoEntityAliasExists('node', $node, 'manually-edited-alias');
$this->assertNoEntityAliasExists('node', $node, 'test-alias');
$this->assertNoEntityAliasExists('node', $node, 'content/node-version-one');
$this->assertNoEntityAliasExists('node', $node, 'content/node-version-two');
}
/**
* Basic functional testing of Pathauto with taxonomy terms.
*/
......@@ -819,11 +900,11 @@ class PathautoBulkUpdateTestCase extends PathautoFunctionalTestHelper {
// Add a new node.
$new_node = $this->drupalCreateNode(array('path' => array('alias' => '', 'pathauto' => FALSE)));
// Run the update again which should only run against the new node.
// Run the update again which should not run against any nodes.
$this->drupalPost('admin/config/search/path/update_bulk', $edit, t('Update'));
$this->assertText('Generated 1 URL alias.'); // 1 node + 0 users
$this->assertText('No new URL aliases to generate.');
$this->assertEntityAliasExists('node', $new_node);
$this->assertNoEntityAliasExists('node', $new_node);
}
}
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment