Newer
Older
<?php
// $Id$

Angie Byron
committed
/**
* @file
* Install, update and uninstall functions for the node module.
*/

Dries Buytaert
committed
* Implements hook_schema().
*/
function node_schema() {
$schema['node'] = array(

Dries Buytaert
committed
'description' => 'The base table for nodes.',
'fields' => array(
'nid' => array(

Dries Buytaert
committed
'description' => 'The primary identifier for a node.',
'type' => 'serial',
'unsigned' => TRUE,
'not null' => TRUE,
),
'vid' => array(

Dries Buytaert
committed
'description' => 'The current {node_revision}.vid version identifier.',
'type' => 'int',
'unsigned' => TRUE,
'not null' => TRUE,
'default' => 0,
),
'type' => array(

Dries Buytaert
committed
'description' => 'The {node_type}.type of this node.',
'type' => 'varchar',
'length' => 32,
'not null' => TRUE,
'default' => '',
),
'language' => array(

Dries Buytaert
committed
'description' => 'The {languages}.language of this node.',
'type' => 'varchar',
'length' => 12,
'not null' => TRUE,
'default' => '',
),
'title' => array(

Dries Buytaert
committed
'description' => 'The title of this node, always treated as non-markup plain text.',
'type' => 'varchar',
'length' => 255,
'not null' => TRUE,
'default' => '',
),
'uid' => array(

Angie Byron
committed
'description' => 'The {users}.uid that owns this node; initially, this is the user that created it.',
'type' => 'int',
'not null' => TRUE,
'default' => 0,
),
'status' => array(

Dries Buytaert
committed
'description' => 'Boolean indicating whether the node is published (visible to non-administrators).',
'type' => 'int',
'not null' => TRUE,
'default' => 1,
),
'created' => array(

Dries Buytaert
committed
'description' => 'The Unix timestamp when the node was created.',
'type' => 'int',
'not null' => TRUE,
'default' => 0,
),
'changed' => array(

Dries Buytaert
committed
'description' => 'The Unix timestamp when the node was most recently saved.',
'type' => 'int',
'not null' => TRUE,
'default' => 0,
),
'comment' => array(

Dries Buytaert
committed
'description' => 'Whether comments are allowed on this node: 0 = no, 1 = closed (read only), 2 = open (read/write).',
'type' => 'int',
'not null' => TRUE,
'default' => 0,
),
'promote' => array(

Dries Buytaert
committed
'description' => 'Boolean indicating whether the node should be displayed on the front page.',
'type' => 'int',
'not null' => TRUE,
'default' => 0,
),
'sticky' => array(

Dries Buytaert
committed
'description' => 'Boolean indicating whether the node should be displayed at the top of lists in which it appears.',
'type' => 'int',
'not null' => TRUE,
'default' => 0,
),
'tnid' => array(

Dries Buytaert
committed
'description' => 'The translation set id for this node, which equals the node id of the source post in each set.',
'type' => 'int',
'unsigned' => TRUE,
'not null' => TRUE,
'default' => 0,
),
'translate' => array(

Dries Buytaert
committed
'description' => 'A boolean indicating whether this translation page needs to be updated.',
'type' => 'int',
'not null' => TRUE,
'default' => 0,
'indexes' => array(
'node_changed' => array('changed'),
'node_created' => array('created'),

Dries Buytaert
committed
'node_frontpage' => array('promote', 'status', 'sticky', 'created'),
'node_status_type' => array('status', 'type', 'nid'),
'node_title_type' => array('title', array('type', 4)),
'node_type' => array(array('type', 4)),
'uid' => array('uid'),
'tnid' => array('tnid'),
'translate' => array('translate'),
'unique keys' => array(
'vid' => array('vid'),
),
'foreign keys' => array(

Dries Buytaert
committed
'node_revision' => array(
'table' => 'node_revision',
'columns' => array('vid' => 'vid'),
),
'node_author' => array(
'table' => 'users',
'columns' => array('uid' => 'uid'),
),
'primary key' => array('nid'),
$schema['node_access'] = array(

Dries Buytaert
committed
'description' => 'Identifies which realm/grant pairs a user must possess in order to view, update, or delete specific nodes.',
'fields' => array(
'nid' => array(

Dries Buytaert
committed
'description' => 'The {node}.nid this record affects.',
'type' => 'int',
'unsigned' => TRUE,
'not null' => TRUE,
'default' => 0,
),
'gid' => array(

Dries Buytaert
committed
'description' => "The grant ID a user must possess in the specified realm to gain this row's privileges on the node.",
'type' => 'int',
'unsigned' => TRUE,
'not null' => TRUE,
'default' => 0,
),
'realm' => array(

Dries Buytaert
committed
'description' => 'The realm in which the user must possess the grant ID. Each node access node can define one or more realms.',
'type' => 'varchar',
'length' => 255,
'not null' => TRUE,
'default' => '',
),
'grant_view' => array(

Dries Buytaert
committed
'description' => 'Boolean indicating whether a user with the realm/grant pair can view this node.',
'type' => 'int',
'unsigned' => TRUE,
'not null' => TRUE,
'default' => 0,
'size' => 'tiny',
),
'grant_update' => array(

Dries Buytaert
committed
'description' => 'Boolean indicating whether a user with the realm/grant pair can edit this node.',
'type' => 'int',
'unsigned' => TRUE,
'not null' => TRUE,
'default' => 0,
'size' => 'tiny',
),
'grant_delete' => array(

Dries Buytaert
committed
'description' => 'Boolean indicating whether a user with the realm/grant pair can delete this node.',
'type' => 'int',
'unsigned' => TRUE,
'not null' => TRUE,
'default' => 0,
'size' => 'tiny',
'primary key' => array('nid', 'gid', 'realm'),

Dries Buytaert
committed
'foreign keys' => array(
'affected_node' => array(
'table' => 'node',
'columns' => array('nid' => 'nid'),
),
),

Dries Buytaert
committed
$schema['node_revision'] = array(

Dries Buytaert
committed
'description' => 'Stores information about each saved version of a {node}.',
'fields' => array(
'nid' => array(

Dries Buytaert
committed
'description' => 'The {node} this version belongs to.',
'type' => 'int',
'unsigned' => TRUE,
'not null' => TRUE,
'default' => 0,
),
'vid' => array(

Dries Buytaert
committed
'description' => 'The primary identifier for this version.',
'type' => 'serial',
'unsigned' => TRUE,
'not null' => TRUE,
),
'uid' => array(

Angie Byron
committed
'description' => 'The {users}.uid that created this version.',
'type' => 'int',
'not null' => TRUE,
'default' => 0,
),
'title' => array(

Dries Buytaert
committed
'description' => 'The title of this version.',
'type' => 'varchar',
'length' => 255,
'not null' => TRUE,
'default' => '',
),
'log' => array(

Dries Buytaert
committed
'description' => 'The log entry explaining the changes in this version.',
'type' => 'text',
'not null' => TRUE,
'size' => 'big',
),
'timestamp' => array(

Dries Buytaert
committed
'description' => 'A Unix timestamp indicating when this version was created.',
'type' => 'int',
'not null' => TRUE,
'default' => 0,
),

Dries Buytaert
committed
'status' => array(
'description' => 'Boolean indicating whether the node (at the time of this revision) is published (visible to non-administrators).',
'type' => 'int',
'not null' => TRUE,
'default' => 1,
),
'comment' => array(
'description' => 'Whether comments are allowed on this node (at the time of this revision): 0 = no, 1 = closed (read only), 2 = open (read/write).',
'type' => 'int',
'not null' => TRUE,
'default' => 0,
),
'promote' => array(
'description' => 'Boolean indicating whether the node (at the time of this revision) should be displayed on the front page.',
'type' => 'int',
'not null' => TRUE,
'default' => 0,
),
'sticky' => array(
'description' => 'Boolean indicating whether the node (at the time of this revision) should be displayed at the top of lists in which it appears.',
'type' => 'int',
'not null' => TRUE,
'default' => 0,
),
'indexes' => array(
'nid' => array('nid'),
'uid' => array('uid'),
),
'primary key' => array('vid'),
'foreign keys' => array(

Dries Buytaert
committed
'versioned_node' => array(
'table' => 'node',
'columns' => array('nid' => 'nid'),
),
'version_author' => array(
'table' => 'users',
'columns' => array('uid' => 'uid'),
),
$schema['node_type'] = array(

Dries Buytaert
committed
'description' => 'Stores information about all defined {node} types.',
'fields' => array(
'type' => array(

Dries Buytaert
committed
'description' => 'The machine-readable name of this type.',
'type' => 'varchar',
'length' => 32,
'not null' => TRUE,
),
'name' => array(

Dries Buytaert
committed
'description' => 'The human-readable name of this type.',
'type' => 'varchar',
'length' => 255,
'not null' => TRUE,
'default' => '',

Dries Buytaert
committed
'translatable' => TRUE,
'base' => array(

Dries Buytaert
committed
'description' => 'The base string used to construct callbacks corresponding to this node type.',
'type' => 'varchar',
'length' => 255,
'not null' => TRUE,
),
'description' => array(

Dries Buytaert
committed
'description' => 'A brief description of this type.',
'type' => 'text',
'not null' => TRUE,
'size' => 'medium',

Dries Buytaert
committed
'translatable' => TRUE,
'help' => array(

Dries Buytaert
committed
'description' => 'Help information shown to the user when creating a {node} of this type.',
'type' => 'text',
'not null' => TRUE,
'size' => 'medium',

Dries Buytaert
committed
'translatable' => TRUE,
'has_title' => array(

Dries Buytaert
committed
'description' => 'Boolean indicating whether this type uses the {node}.title field.',
'type' => 'int',
'unsigned' => TRUE,
'not null' => TRUE,
'size' => 'tiny',
),
'title_label' => array(

Dries Buytaert
committed
'description' => 'The label displayed for the title field on the edit form.',
'type' => 'varchar',
'length' => 255,
'not null' => TRUE,
'default' => '',

Dries Buytaert
committed
'translatable' => TRUE,
'custom' => array(
'description' => 'A boolean indicating whether this type is defined by a module (FALSE) or by a user via Add content type (TRUE).',
'type' => 'int',
'not null' => TRUE,
'default' => 0,
'size' => 'tiny',
),
'modified' => array(

Dries Buytaert
committed
'description' => 'A boolean indicating whether this type has been modified by an administrator; currently not used in any way.',
'type' => 'int',
'not null' => TRUE,
'default' => 0,
'size' => 'tiny',
),
'locked' => array(

Dries Buytaert
committed
'description' => 'A boolean indicating whether the administrator can change the machine name of this type.',
'type' => 'int',
'not null' => TRUE,
'default' => 0,
'size' => 'tiny',
),
'orig_type' => array(

Dries Buytaert
committed
'description' => 'The original machine-readable name of this node type. This may be different from the current type name if the locked field is 0.',
'type' => 'varchar',
'length' => 255,
'not null' => TRUE,
'default' => '',
'primary key' => array('type'),

Dries Buytaert
committed
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
$schema['block_node_type'] = array(
'description' => 'Sets up display criteria for blocks based on content types',
'fields' => array(
'module' => array(
'type' => 'varchar',
'length' => 64,
'not null' => TRUE,
'description' => "The block's origin module, from {block}.module.",
),
'delta' => array(
'type' => 'varchar',
'length' => 32,
'not null' => TRUE,
'description' => "The block's unique delta within module, from {block}.delta.",
),
'type' => array(
'type' => 'varchar',
'length' => 32,
'not null' => TRUE,
'description' => "The machine-readable name of this type from {node_type}.type.",
),
),
'primary key' => array('module', 'delta', 'type'),
'indexes' => array(
'type' => array('type'),
),
);
return $schema;
}

Dries Buytaert
committed
/**
* Implements hook_install().
*/
function node_install() {
// Populate the node access table.
db_insert('node_access')
->fields(array(
'nid' => 0,
'gid' => 0,
'realm' => 'all',
'grant_view' => 1,
'grant_update' => 0,
'grant_delete' => 0,
))
->execute();
}

Angie Byron
committed
/**
* Implements hook_update_dependencies().
*/
function node_update_dependencies() {
// Node update 7006 migrates node data to fields and therefore must run after
// the Field module has been enabled, but before upgrading field data.
$dependencies['node'][7006] = array(
'system' => 7049,
);
$dependencies['system'][7050] = array(
'node' => 7006,
);
return $dependencies;
}
/**

Dries Buytaert
committed
* @defgroup updates-6.x-to-7.x System updates from 6.x to 7.x
* @{
*/
/**
* Fix node type 'module' attribute to avoid name-space conflicts.
*/
function node_update_7000() {

Dries Buytaert
committed
db_update('node_type')
->fields(array('module' => 'node_content'))
->condition('module', 'node')
->execute();

Angie Byron
committed
// Rename the module column to base.
db_change_field('node_type', 'module', 'base', array('type' => 'varchar', 'length' => 255, 'not null' => TRUE));
}

Dries Buytaert
committed
/**
* Rename {node_revisions} table to {node_revision}.
*/
function node_update_7001() {

Dries Buytaert
committed
db_rename_table('node_revisions', 'node_revision');

Dries Buytaert
committed
}

Dries Buytaert
committed
/**
* Extend the node_promote_status index to include all fields required for the node page query.
*/
function node_update_7002() {

Dries Buytaert
committed
db_drop_index('node', 'node_promote_status');
db_add_index('node', 'node_frontpage', array('promote', 'status', 'sticky', 'created'));

Dries Buytaert
committed
}
/**

Dries Buytaert
committed
* Remove the node_counter if the statistics module is uninstalled.
*/
function node_update_7003() {

Dries Buytaert
committed
if (drupal_get_installed_schema_version('statistics') == SCHEMA_UNINSTALLED) {

Dries Buytaert
committed
db_drop_table('node_counter');

Dries Buytaert
committed
}
}

Dries Buytaert
committed
/**
* Extend the existing default preview and teaser settings to all node types.
*/
function node_update_7004() {
// Get original settings and all types.
$original_length = variable_get('teaser_length', 600);
$original_preview = variable_get('node_preview', 0);
// Map old preview setting to new values order.
$original_preview ? $original_preview = 2 : $original_preview = 1;

Angie Byron
committed
drupal_static_reset('_node_types_build');
$type_list = node_type_get_types();

Dries Buytaert
committed
// Apply original settings to all types.
foreach ($type_list as $type => $entity) {

Dries Buytaert
committed
variable_set('teaser_length_' . $type, $original_length);
variable_set('node_preview_' . $type, $original_preview);
}
// Delete old variable but leave 'teaser_length' for aggregator module upgrade.
variable_del('node_preview');
}

Dries Buytaert
committed
/**
* Add status/comment/promote and sticky columns to the {node_revision} table.

Dries Buytaert
committed
*/

Dries Buytaert
committed
function node_update_7005() {
foreach(array('status', 'comment', 'promote', 'sticky') as $column) {

Dries Buytaert
committed
db_add_field('node_revision', $column, array(

Dries Buytaert
committed
'type' => 'int',
'not null' => TRUE,
'default' => 0,
));
}
}
/**
* Convert body and teaser from node properties to fields, and migrate status/comment/promote and sticky columns to the {node_revision} table.

Dries Buytaert
committed
*/

Angie Byron
committed
function node_update_7006(&$sandbox) {
$sandbox['#finished'] = 0;

Dries Buytaert
committed
// Get node type info for every invocation.

Angie Byron
committed
drupal_static_reset('_node_types_build');

Dries Buytaert
committed
$node_types = node_type_get_types();

Angie Byron
committed
if (!isset($sandbox['total'])) {

Dries Buytaert
committed
// Initial invocation.

Angie Byron
committed
// Get node type info, specifically the body field settings.
$result = db_select('node_type', 'node_type')
->fields('node_type')
->execute();
foreach ($result as $type_object) {
$node_types[$type_object->type] = $type_object;
}
$extra_types = db_query('SELECT DISTINCT type FROM {node} WHERE type NOT IN (:types)', array(':types' => array_keys($node_types)))->fetchCol();
foreach ($extra_types as $type) {
$type_object = new stdClass;
$type_object->type = $type;
// Always create a body. Querying node_revisions for a non-empty body
// would skip creating body fields for types that have a body but
// the nodes of that type so far had empty bodies.
$type_object->has_body = 1;
$type_object->body_label = 'Body';
$node_types[$type_object->type] = $type_object;
}

Angie Byron
committed

Dries Buytaert
committed
$default_trim_length = variable_get('teaser_length', 600);

Angie Byron
committed
// Add body field instances for existing node types.
foreach ($node_types as $node_type) {

Angie Byron
committed
if ($node_type->has_body) {

Dries Buytaert
committed
$instance = node_add_body_field($node_type, $node_type->body_label);
// Update newly created instance to convert teaser_length variable
// into formatter settings.
$trim_length = variable_get('teaser_length_' . $node_type->type, $default_trim_length);
$instance_changed = FALSE;
foreach ($instance['display'] as $view_mode => $view_mode_info) {
if ($view_mode_info['type'] == 'text_trimmed' || $view_mode_info['type'] == 'text_summary_or_trimmed') {
if (!isset($view_mode_info['settings']['trim_length'])) {
$instance['display'][$view_mode]['settings']['trim_length'] = $trim_length;
$instance_changed = TRUE;
}
}
}
if ($instance_changed) {
field_update_instance($instance);
}
variable_del('teaser_length_' . $node_type->type);

Dries Buytaert
committed
}

Dries Buytaert
committed
// Leave 'teaser_length' variable for aggregator module upgrade.

Angie Byron
committed
$sandbox['node_types_info'][$node_type->type] = array(
'has_body' => $node_type->has_body,
);

Dries Buytaert
committed
}
// Initialize state for future calls.

Angie Byron
committed
$sandbox['last'] = 0;
$sandbox['count'] = 0;

Dries Buytaert
committed
$query = db_select('node', 'n');
$query->join('node_revision', 'nr', 'n.vid = nr.vid');

Angie Byron
committed
$sandbox['total'] = $query->countQuery()->execute()->fetchField();

Dries Buytaert
committed
}
else {
// Subsequent invocations.
// Grab the body field ID for field_sql_storage_field_storage_write().

Angie Byron
committed
$body_field = field_info_field('body');
$body_field_id = $body_field['id'];

Dries Buytaert
committed
$found = FALSE;

Angie Byron
committed
if ($sandbox['total']) {

Dries Buytaert
committed
// Operate on every revision of every node (whee!), in batches.
$batch_size = 200;
$query = db_select('node_revision', 'nr');
$query->innerJoin('node', 'n', 'n.vid = nr.vid');
$query
->fields('nr', array('nid', 'vid', 'body', 'teaser', 'format'))

Angie Byron
committed
->fields('n', array('type', 'status', 'comment', 'promote', 'sticky', 'language'))

Angie Byron
committed
->condition('nr.vid', $sandbox['last'], '>')

Dries Buytaert
committed
->orderBy('nr.vid', 'ASC')
->range(0, $batch_size);
$revisions = $query->execute();

Dries Buytaert
committed
// Load each reversion of each node, set up 'body'
// appropriately, and save the node's field data. Note that
// node_load() will not return the body or teaser values from
// {node_revision} because those columns have been removed from the
// schema structure in memory (but not yet from the database),
// so we get the values from the explicit query of the table
// instead.
foreach ($revisions as $revision) {
$found = TRUE;
if ($sandbox['node_types_info'][$revision->type]['has_body']) {

Dries Buytaert
committed
$node = (object) array(
'nid' => $revision->nid,
'vid' => $revision->vid,
'type' => $revision->type,
);

Angie Byron
committed
// After node_update_7009() we will always have LANGUAGE_NONE as
// language neutral language code, but here we still have empty
// strings.
$langcode = empty($revision->language) ? LANGUAGE_NONE : $revision->language;

Dries Buytaert
committed
if (!empty($revision->teaser) && $revision->teaser != text_summary($revision->body)) {

Angie Byron
committed
$node->body[$langcode][0]['summary'] = $revision->teaser;

Dries Buytaert
committed
}
// Do this after text_summary() above.
$break = '<!--break-->';
if (substr($revision->body, 0, strlen($break)) == $break) {
$revision->body = substr($revision->body, strlen($break));
}

Angie Byron
committed
$node->body[$langcode][0]['value'] = $revision->body;
// Explicitly store the current default text format if the revision
// did not have its own text format. Similar conversions for other
// core modules are performed in filter_update_7005(), but we do this
// one here since we are already migrating the data.

Angie Byron
committed
$node->body[$langcode][0]['format'] = !empty($revision->format) ? $revision->format : variable_get('filter_default_format', 1);

Dries Buytaert
committed
// This is a core update and no contrib modules are enabled yet, so
// we can assume default field storage for a faster update.

Angie Byron
committed
field_sql_storage_field_storage_write('node', $node, FIELD_STORAGE_INSERT, array($body_field_id));

Dries Buytaert
committed
}

Dries Buytaert
committed
// Migrate the status columns to the {node_revision} table.
db_update('node_revision')
->fields(array(
'status' => $revision->status,
'comment' => $revision->comment,
'promote' => $revision->promote,
'sticky' => $revision->sticky,
))
->condition('vid', $revision->vid)
->execute();

Angie Byron
committed
$sandbox['last'] = $revision->vid;
$sandbox['count'] += 1;

Dries Buytaert
committed
}

Angie Byron
committed
$sandbox['#finished'] = min(0.99, $sandbox['count'] / $sandbox['total']);

Dries Buytaert
committed
}
if (!$found) {
// All nodes are processed.
// Remove the now-obsolete body info from node_revision.

Dries Buytaert
committed
db_drop_field('node_revision', 'body');
db_drop_field('node_revision', 'teaser');
db_drop_field('node_revision', 'format');

Dries Buytaert
committed

Angie Byron
committed
// Remove node_type properties related to the former 'body'.
db_drop_field('node_type', 'has_body');
db_drop_field('node_type', 'body_label');

Dries Buytaert
committed
// We're done.

Angie Byron
committed
$sandbox['#finished'] = 1;

Dries Buytaert
committed
}
}
}

Dries Buytaert
committed
/**
* Remove column min_word_count.
*/

Dries Buytaert
committed
function node_update_7007() {

Dries Buytaert
committed
db_drop_field('node_type', 'min_word_count');

Dries Buytaert
committed
}

Dries Buytaert
committed
/**
* Split the 'administer nodes' permission from 'access content overview'.
*/
function node_update_7008() {
$roles = user_roles(FALSE, 'administer nodes');
foreach ($roles as $rid => $role) {
user_role_grant_permissions($rid, array('access content overview'));
}
}
/**
* Convert node languages from the empty string to LANGUAGE_NONE.
*/
function node_update_7009() {
db_update('node')
->fields(array('language' => LANGUAGE_NONE))
->condition('language', '')
->execute();
}

Dries Buytaert
committed
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
/**
* Add the {block_node_type} table.
*/
function node_update_7010() {
$schema['block_node_type'] = array(
'description' => 'Sets up display criteria for blocks based on content types',
'fields' => array(
'module' => array(
'type' => 'varchar',
'length' => 64,
'not null' => TRUE,
'description' => "The block's origin module, from {block}.module.",
),
'delta' => array(
'type' => 'varchar',
'length' => 32,
'not null' => TRUE,
'description' => "The block's unique delta within module, from {block}.delta.",
),
'type' => array(
'type' => 'varchar',
'length' => 32,
'not null' => TRUE,
'description' => "The machine-readable name of this type from {node_type}.type.",
),
),
'primary key' => array('module', 'delta', 'type'),
'indexes' => array(
'type' => array('type'),
),
);
db_create_table('block_node_type', $schema['block_node_type']);
}

Dries Buytaert
committed
/**
* @} End of "defgroup updates-6.x-to-7.x"
* The next series of updates should start at 8000.