=== added file 'modules/content.module' --- /dev/null +++ modules/content.module @@ -0,0 +1,115 @@ + $type) { + $perms[] = 'create '. $name .' content'; + $perms[] = 'edit own '. $name .' content'; + $perms[] = 'edit '. $name .' content'; + } + return $perms; +} + +/** + * Implementation of hook_menu(). + */ +function content_menu($may_cache) { + $items = array(); + $access = user_access('administer content types'); + + // Only include administrative callbacks if we are viewing an admin page. + if (arg(0) == 'admin' && arg(1) == 'node' && arg(2) == 'types') { + include_once(drupal_get_path('module', 'content') .'/content_admin.inc'); + } + + if (!$may_cache) { + $items[] = array( + if ($access && arg(0) == 'admin' && arg(1) == 'node' && arg(2) == 'types' && arg(3)) { + $types = _content_types(); + if (isset($types[arg(3)])) { + $items[] = array( + 'path' => 'admin/node/types/'. arg(3) .'/delete', + 'title' => t('delete'), + 'callback' => '_content_admin_type_delete', + 'access' => $access, + 'callback arguments' => array(arg(3)), + 'type' => MENU_CALLBACK, + ); + } + } + } + + return $items; +} + +/** + * Implementation of hook_access(). + */ +function content_access($op, $node) { + global $user; + $type = is_string($node) ? $node : (is_array($node) ? $node['type'] : $node->type); + + if ($op == 'create') { + return user_access('create '. $type .' content'); + } + + if ($op == 'update' || $op == 'delete') { + if (user_access('edit '. $type .' content') || (user_access('edit own '. $type .' content') && ($user->uid == $node->uid))) { + return TRUE; + } + } +} + +/** + * Implementation of hook_form(). + * + * Each field defines its own component of the content entry form, via its + * chosen widget. + */ +function content_form($node) { + $types = _content_types(); + $type = $types[$node->type]; + + if (!empty($type->title_label)) { + $form['title'] = array( + '#type' => 'textfield', + '#title' => check_plain(t($type->title_label)), + '#required' => TRUE, + '#default_value' => $node->title, + '#weight' => -5, + ); + } + + if (!empty($type->body_label)) { + $form['body_filter']['body'] = array( + '#type' => 'textarea', + '#title' => check_plain(t($type->body_label)), + '#default_value' => $node->body, + '#rows' => 20, + '#required' => TRUE); + $form['body_filter']['format'] = filter_form($node->format); + } + + + return $form; +} + === added file 'modules/content_admin.inc' --- /dev/null +++ modules/content_admin.inc @@ -0,0 +1,149 @@ +label = ''; + $type->description = ''; + $type->help = ''; + $type->title_label = t('Title'); + $type->body_label = t('Body'); + } + } + + $form = array(); + $form['label'] = array( + '#title' => t('Label'), + '#type' => 'textfield', + '#default_value' => $type->label, + '#description' => t('The human-readable name of this content type.'), + '#required' => TRUE, + ); + $form['description'] = array( + '#title' => t('Description'), + '#type' => 'textarea', + '#default_value' => $type->description, + '#rows' => 10, + '#description' => t('A brief description of the content type.'), + '#required' => FALSE, + ); + $form['help'] = array( + '#title' => t('Help text'), + '#type' => 'textarea', + '#default_value' => $type->help, + '#rows' => 10, + '#description' => t('Instructions to present to the user when adding new content of this type.'), + '#required' => FALSE, + ); + $form['title_label'] = array( + '#title' => t('Title field label'), + '#type' => 'textfield', + '#default_value' => $type->title_label, + '#description' => t('The label for the title field.'), + '#required' => TRUE, + ); + $form['body_label'] = array( + '#title' => t('Body field label'), + '#type' => 'textfield', + '#default_value' => $type->body_label, + '#description' => t('The label for the body field.'), + '#required' => TRUE, + ); + $form['submit'] = array( + '#type' => 'submit', + '#value' => t('Save content type'), + ); + $form['type_name'] = array( + '#type' => 'value', + '#value' => $type_name, + ); + $form['original_type_name'] = array( + '#type' => 'value', + '#value' => $original_type_name, + ); + return drupal_get_form('_content_admin_type_edit', $form); +} + +/** + * Save a content type after editing. + */ +function _content_admin_type_edit_submit($form_id, $form_values) { +$types = _content_types(); + if (!isset($types[$form_values['type_name']])) { + // Find a valid, computer-friendly type name. + $form_values['type_name'] = trim($form_values['label']); + $form_values['type_name'] = drupal_strtolower($form_values['type_name']); + $form_values['type_name'] = str_replace(array(' ', '-'), '_', $form_values['type_name']); + $form_values['type_name'] = preg_replace('/[^a-z0-9_]/', '', $form_values['type_name']); + $form_values['type_name'] = 'content-'. $form_values['type_name']; + $form_values['type_name'] = substr($form_values['type_name'], 0, 32); + if (isset($types[$form_values['type_name']])) { + $counter = 0; + do { + $new_name = substr($form_values['type_name'], 0, 30) .'_'. $counter++; + } while (isset($types[$new_name])); + $form_values['type_name'] = $new_name; + } + + db_query("INSERT INTO {node_type} (type_name, label, description, help, title_label, body_label) VALUES ('%s', '%s', '%s', '%s', '%s', '%s')", $form_values['type_name'], $form_values['label'], $form_values['description'], $form_values['help'], $form_values['title_label'], $form_values['body_label']); + } + else { + db_query("UPDATE {node_type} SET label = '%s', description = '%s', help = '%s', title_label = '%s', body_label = '%s' WHERE type_name = '%s'", $form_values['label'], $form_values['description'], $form_values['help'], $form_values['title_label'], $form_values['body_label'], $form_values['type_name']); + } + + drupal_set_message(t('Saved content type %type.', array('%type' => theme('placeholder', $form_values['label'])))); + + content_clear_type_cache(); + menu_rebuild(); + return 'admin/node/types'; +} + +/** + * Menu callback; delete a content type. + */ +function _content_admin_type_delete($type_name = '') { + $types = _content_types(); + $type = $types[$type_name]; + + $form = array(); + $form['type_name'] = array('#type' => 'value', '#value' => $type_name); + return confirm_form('_content_admin_type_delete', $form, t('Are you sure you want to delete the content type %type?', array('%type' => theme('placeholder', $type->label))), 'admin/node/types', t('If you have any content left in this content type, it will be permanently deleted. This action cannot be undone.'), t('Delete'), t('Cancel')); +} + +/** + * Delete a content type. + */ +function _content_admin_type_delete_submit($form_id, $form_values) { + $types = _content_types(); + $type = $types[$form_values['type_name']]; + + if ($type && $form_values['confirm']) { + // Delete all nodes of this content type. + $result = db_query("SELECT nid FROM {node} WHERE type = '%s'", $form_values['type_name']); + while ($node = db_fetch_object($result)) { + node_delete($node->nid); + } + db_query("DELETE FROM {node_type} WHERE type_name = '%s'", $form_values['type_name']); + drupal_set_message(t('Deleted content type %type.', array('%type' => theme('placeholder', $type->label)))); + content_clear_type_cache(); + drupal_goto('admin/node/types'); + } +} === modified file 'modules/node.module' --- modules/node.module +++ modules/node.module @@ -195,16 +195,26 @@ function node_teaser($body, $format = NU return truncate_utf8($body, $size); } -function _node_names($op = '', $node = NULL) { - static $node_names = array(); - static $node_list = array(); - - if (empty($node_names)) { - $node_names = module_invoke_all('node_info'); - foreach ($node_names as $type => $value) { - $node_list[$type] = $value['name']; +function _node_types($op = '', $node = NULL, $reset = FALSE) { + static $node_types, $node_labels; + + if ($reset || !isset($node_types)) { + if ($cached = cache_get('node_types')) { + $node_types = unserialize($cached->data); + } + else { + $node_types = array(); + $type_result = db_query('SELECT * FROM {node_type} nt ORDER BY nt.type_name ASC'); + while ($type = db_fetch_object($type_result)) { + $type->fields = module_invoke('field', 'load_type', $type->type_name); + $node_types[$type->type_name] = $type; + $node_labels[$type->type_name] = $type->label; + } + + cache_set('node_types', serialize($node_types), CACHE_PERMANENT); } } + if ($node) { if (is_array($node)) { $type = $node['type']; @@ -215,17 +225,19 @@ function _node_names($op = '', $node = N elseif (is_string($node)) { $type = $node; } - if (!isset($node_names[$type])) { + if (!isset($node_types[$type])) { return FALSE; } } switch ($op) { + case 'types': + return $node_types; case 'base': - return $node_names[$type]['base']; + return $node_types[$type]->base; case 'list': - return $node_list; - case 'name': - return $node_list[$type]; + return $node_labels; + case 'label': + return $node_labels[$type]; } } @@ -238,7 +250,7 @@ function _node_names($op = '', $node = N * The basename for hook_load, hook_nodeapi etc. */ function node_get_base($node) { - return _node_names('base', $node); + return _node_types('base', $node); } /** @@ -249,8 +261,8 @@ function node_get_base($node) { * @return * The human readable name of the node type. */ -function node_get_name($node) { - return _node_names('name', $node); +function node_get_label($node) { + return _node_types('label', $node); } /** @@ -259,12 +271,36 @@ function node_get_name($node) { * @param $node * Either a node object, a node array, or a string containing the node type. * @return - * An array consisting ('#type' => name) pairs. + * An array consisting (machine readable node name => node type object) pairs. */ function node_get_types() { - return _node_names('list'); + return _node_types('types'); +} + +/** + * Return the list of available node types. + * + * @param $node + * Either a node object, a node array, or a string containing the node type. + * @return + * An array consisting (machine readable node name => human readable node name) pairs. + */ +function node_get_list() { + return _node_types('list'); +} + +/** + * Clear the cache of node_types; called in several places when content + * information is changed. + */ +function content_clear_type_cache() { + cache_clear_all('node_types'); + + _content_types(TRUE); + module_invoke('field', 'clear_type_cache'); } + /** * Determine whether a node hook exists. * @@ -721,7 +757,7 @@ function node_search($op = 'search', $ke $extra = node_invoke_nodeapi($node, 'search result'); $results[] = array('link' => url('node/'. $item->sid), - 'type' => node_get_name($node), + 'type' => node_get_label($node), 'title' => $node->title, 'user' => theme('username', $node), 'date' => $node->changed, @@ -888,7 +924,7 @@ function node_menu($may_cache) { } else if (arg(0) == 'admin' && arg(1) == 'settings' && arg(2) == 'content-types' && is_string(arg(3))) { $items[] = array('path' => 'admin/settings/content-types/'. arg(3), - 'title' => t("'%name' content type", array('%name' => node_get_name(arg(3)))), + 'title' => t("'%name' content type", array('%name' => node_get_label(arg(3)))), 'type' => MENU_CALLBACK); } } @@ -926,7 +962,7 @@ function node_filters() { 'moderate-1' => t('in moderation'), 'moderate-0' => t('not in moderation'), 'promote-1' => t('promoted'), 'promote-0' => t('not promoted'), 'sticky-1' => t('sticky'), 'sticky-0' => t('not sticky'))); - $filters['type'] = array('title' => t('type'), 'options' => node_get_types()); + $filters['type'] = array('title' => t('type'), 'options' => node_get_list()); // The taxonomy filter if ($taxonomy = module_invoke('taxonomy', 'form_all', 1)) { $filters['category'] = array('title' => t('category'), 'options' => $taxonomy); @@ -1141,7 +1177,7 @@ function node_admin_nodes() { while ($node = db_fetch_object($result)) { $nodes[$node->nid] = ''; $form['title'][$node->nid] = array('#value' => l($node->title, 'node/'. $node->nid) .' '. theme('mark', node_mark($node->nid, $node->changed))); - $form['name'][$node->nid] = array('#value' => node_get_name($node)); + $form['name'][$node->nid] = array('#value' => node_get_label($node)); $form['username'][$node->nid] = array('#value' => theme('username', $node)); $form['status'][$node->nid] = array('#value' => ($node->status ? t('published') : t('not published'))); $form['operations'][$node->nid] = array('#value' => l(t('edit'), 'node/'. $node->nid .'/edit', array(), $destination)); @@ -1227,11 +1263,11 @@ function node_types_configure($type = NU $form['submission'] = array('#type' => 'fieldset', '#title' =>t('Submission form') ); $form['submission'][$type . '_help'] = array( '#type' => 'textarea', '#title' => t('Explanation or submission guidelines'), '#default_value' => variable_get($type .'_help', ''), - '#description' => t('This text will be displayed at the top of the %type submission form. It is useful for helping or instructing your users.', array('%type' => node_get_name($type))) + '#description' => t('This text will be displayed at the top of the %type submission form. It is useful for helping or instructing your users.', array('%type' => node_get_label($type))) ); $form['submission']['minimum_'. $type .'_size'] = array( '#type' => 'select', '#title' => t('Minimum number of words'), '#default_value' => variable_get('minimum_'. $type .'_size', 0), '#options' => drupal_map_assoc(array(0, 10, 25, 50, 75, 100, 125, 150, 175, 200)), - '#description' => t('The minimum number of words a %type must be to be considered valid. This can be useful to rule out submissions that do not meet the site\'s standards, such as short test posts.', array('%type' => node_get_name($type))) + '#description' => t('The minimum number of words a %type must be to be considered valid. This can be useful to rule out submissions that do not meet the site\'s standards, such as short test posts.', array('%type' => node_get_label($type))) ); $form['workflow'] = array('#type' => 'fieldset', '#title' =>t('Workflow')); $form['type'] = array('#type' => 'value', '#value' => $type); @@ -1243,8 +1279,22 @@ function node_types_configure($type = NU $header = array(t('Type'), t('Operations')); $rows = array(); - foreach (node_get_types() as $type => $name) { - $rows[] = array($name, l(t('configure'), 'admin/settings/content-types/'. $type)); + + foreach (node_get_types() as $label => $type) { + $base = 'admin/settings/content-types/'. $type->name; + $row = array(); + $row[] = $type->label; + $row[] = truncate_utf8(filter_xss_admin($type->description), 60); + $row[] = l(t('configure'), $base); + if (module_exists('content')) { + $row[] = l(t($type->label), $base .'/fields'); + $row[] = l(t('duplicate'), $base .'/duplicate'); + } + if ($type->base == 'content') { + $row[] = l(t('delete'), $base .'/delete'); + } + $rows[] = $row; + } return theme('table', $header, $rows); @@ -1529,7 +1579,7 @@ function node_validate($node, $form = ar // Make sure the body has the minimum number of words. // todo use a better word counting algorithm that will work in other languages if (isset($node->body) && count(explode(' ', $node->body)) < variable_get('minimum_'. $node->type .'_size', 0)) { - form_set_error('body', t('The body of your %type is too short. You need at least %words words.', array('%words' => variable_get('minimum_'. $node->type .'_size', 0), '%type' => node_get_name($node)))); + form_set_error('body', t('The body of your %type is too short. You need at least %words words.', array('%words' => variable_get('minimum_'. $node->type .'_size', 0), '%type' => node_get_label($node)))); } if (isset($node->nid) && (node_last_changed($node->nid) > $node->changed)) { @@ -1703,26 +1753,27 @@ function theme_node_form($form) { function node_add($type) { global $user; + $types = node_get_types(); // If a node type has been specified, validate its existence. - if (array_key_exists($type, node_get_types()) && node_access('create', $type)) { + if (isset($types[$type]) && node_access('create', $type)) { // Initialize settings: $node = array('uid' => $user->uid, 'name' => $user->name, 'type' => $type); $output = node_form($node); - drupal_set_title(t('Submit %name', array('%name' => node_get_name($node)))); + drupal_set_title(t('Submit %name', array('%name' => $types[$type]->label))); } else { // If no (valid) node type has been provided, display a node type overview. - foreach (node_get_types() as $type => $name) { - if (module_invoke(node_get_base($type), 'access', 'create', $type)) { - $out = '