| Commit | Line | Data |
|---|---|---|
| 9a7f744c JV |
1 | <?php |
| 2 | // $Id$ | |
| 9a7f744c JV |
3 | /** |
| 4 | * @file | |
| 124988a2 | 5 | * Allows administrators to associate custom fields to content types. |
| 9a7f744c JV |
6 | */ |
| 7 | ||
| e30b4a0a YC |
8 | define('CONTENT_DB_STORAGE_PER_FIELD', 0); |
| 9 | define('CONTENT_DB_STORAGE_PER_CONTENT_TYPE', 1); | |
| c46b3c82 | 10 | |
| 429d94dc YC |
11 | define('CONTENT_CALLBACK_NONE', 0x0001); |
| 12 | define('CONTENT_CALLBACK_DEFAULT', 0x0002); | |
| 13 | define('CONTENT_CALLBACK_CUSTOM', 0x0004); | |
| 14 | ||
| a4418efc KS |
15 | define('CONTENT_HANDLE_CORE', 0x0001); |
| 16 | define('CONTENT_HANDLE_MODULE', 0x0002); | |
| 17 | ||
| b01b0973 | 18 | function content_help($path, $arg) { |
| 721ef771 YC |
19 | switch ($path) { |
| 20 | case 'admin/help#content': | |
| 21 | $output = '<p>'. t('The content module, a required component of the Content Construction Kit (CCK), allows administrators to associate custom fields with content types. In Drupal, content types are used to define the characteristics of a post, including the title and description of the fields displayed on its add and edit pages. Using the content module (and the other helper modules included in CCK), custom fields beyond the default "Title" and "Body" may be added. CCK features are accessible through tabs on the <a href="@content-types">content types administration page</a>. (See the <a href="@node-help">node module help page</a> for more information about content types.)', array('@content-types' => url('admin/content/types'), '@node-help' => url('admin/help/node'))) .'</p>'; | |
| 22 | $output .= '<p>'. t('When adding a custom field to a content type, you determine its type (whether it will contain text, numbers, or references to other objects) and how it will be displayed (either as a text field or area, a select box, checkbox, radio button, or autocompleting field). A field may have multiple values (i.e., a "person" may have multiple e-mail addresses) or a single value (i.e., an "employee" has a single employee identification number). As you add and edit fields, CCK automatically adjusts the structure of the database as necessary. CCK also provides a number of other features, including intelligent caching for your custom data, an import and export facility for content type definitions, and integration with other contributed modules.') .'</p>'; | |
| 23 | $output .= '<p>'. t('Custom field types are provided by a set of optional modules included with CCK (each module provides a different type). The <a href="@modules">modules page</a> allows you to enable or disable CCK components. A default installation of CCK includes:', array('@modules' => url('admin/build/modules'))) .'</p>'; | |
| 24 | $output .= '<ul>'; | |
| 25 | $output .= '<li>'. t('<em>number</em>, which adds numeric field types, in integer, decimal or floating point form. You may define a set of allowed inputs, or specify an allowable range of values. A variety of common formats for displaying numeric data are available.') .'</li>'; | |
| 26 | $output .= '<li>'. t("<em>text</em>, which adds text field types. A text field may contain plain text only, or optionally, may use Drupal's input format filters to securely manage rich text input. Text input fields may be either a single line (text field), multiple lines (text area), or for greater input control, a select box, checkbox, or radio buttons. If desired, CCK can validate the input to a set of allowed values.") .'</li>'; | |
| 27 | $output .= '<li>'. t('<em>nodereference</em>, which creates custom references between Drupal nodes. By adding a <em>nodereference</em> field and two different content types, for instance, you can easily create complex parent/child relationships between data (multiple "employee" nodes may contain a <em>nodereference</em> field linking to an "employer" node).') .'</li>'; | |
| 28 | $output .= '<li>'. t('<em>userreference</em>, which creates custom references to your sites\' user accounts. By adding a <em>userreference</em> field, you can create complex relationships between your site\'s users and posts. To track user involvement in a post beyond Drupal\'s standard <em>Authored by</em> field, for instance, add a <em>userreference</em> field named "Edited by" to a content type to store a link to an editor\'s user account page.') .'</li>'; | |
| 29 | $output .= '<li>'. t('<em>fieldgroup</em>, which creates collapsible fieldsets to hold a group of related fields. A fieldset may either be open or closed by default. The order of your fieldsets, and the order of fields within a fieldset, is managed via a drag-and-drop interface provided by content module.') .'</li>'; | |
| 30 | $output .= '</ul>'; | |
| 31 | $output .= '<p>'. t('For more information, see the online handbook entry for <a href="@handbook-cck">CCK</a> or the <a href="@project-cck">CCK project page</a>.', array('@handbook-cck' => 'http://drupal.org/handbook/modules/cck', '@project-cck' => 'http://drupal.org/project/cck')) .'</p>'; | |
| 32 | return $output; | |
| 33 | } | |
| 9e301bfe YC |
34 | } |
| 35 | ||
| 1037bde7 | 36 | /** |
| 3c4f77f2 YC |
37 | * Implementation of hook_flush_caches. |
| 38 | */ | |
| 39 | function content_flush_caches() { | |
| 40 | return array(content_cache_tablename()); | |
| 41 | } | |
| 42 | ||
| 43 | /** | |
| 1037bde7 KS |
44 | * Implementation of hook_init(). |
| 45 | */ | |
| 46 | function content_init() { | |
| 172281af | 47 | drupal_add_css(drupal_get_path('module', 'content') .'/theme/content-module.css'); |
| 7d47bfe1 | 48 | if (module_exists('token') && !function_exists('content_token_values')) { |
| ddabf7ba | 49 | module_load_include('inc', 'content', 'includes/content.token'); |
| 6e023c8c | 50 | } |
| 023ba5bc | 51 | if (module_exists('diff') && !function_exists('content_diff')) { |
| ddabf7ba | 52 | module_load_include('inc', 'content', 'includes/content.diff'); |
| 31e338ec | 53 | } |
| ab3623e6 KS |
54 | } |
| 55 | ||
| 56 | /** | |
| 55a17f0e YC |
57 | * Implementation of hook_perm(). |
| 58 | */ | |
| 59 | function content_perm() { | |
| 224f4949 | 60 | return array('Use PHP input for field settings (dangerous - grant with care)'); |
| 55a17f0e YC |
61 | } |
| 62 | ||
| 63 | /** | |
| 9f386144 YC |
64 | * Implementation of hook_menu_alter(). |
| 65 | */ | |
| 66 | function content_menu_alter(&$items) { | |
| 67 | // Customize the content types page with our own callback | |
| 745c9aba | 68 | $items['admin/content/types']['page callback'] = 'content_types_overview'; |
| 9f386144 YC |
69 | $items['admin/content/types']['file'] = 'content.admin.inc'; |
| 70 | $items['admin/content/types']['file path'] = drupal_get_path('module', 'content') .'/includes'; | |
| 71 | } | |
| 72 | ||
| 73 | /** | |
| 9a7f744c JV |
74 | * Implementation of hook_menu(). |
| 75 | */ | |
| 4a6e00e0 | 76 | function content_menu() { |
| 9a7f744c | 77 | $items = array(); |
| 4a6e00e0 | 78 | $items['admin/content/types/fields'] = array( |
| b01b0973 | 79 | 'title' => 'Fields', |
| 2c175854 | 80 | 'page callback' => 'content_fields_list', |
| 4a6e00e0 | 81 | 'access arguments' => array('administer content types'), |
| ddabf7ba | 82 | 'file' => 'includes/content.admin.inc', |
| 4a6e00e0 YC |
83 | 'type' => MENU_LOCAL_TASK, |
| 84 | ); | |
| 57e83bcc YC |
85 | // Callback for AHAH add more buttons. |
| 86 | $items['content/js_add_more'] = array( | |
| 87 | 'page callback' => 'content_add_more_js', | |
| 57e83bcc | 88 | 'access arguments' => array('access content'), |
| ddabf7ba | 89 | 'file' => 'includes/content.node_form.inc', |
| 57e83bcc YC |
90 | 'type' => MENU_CALLBACK, |
| 91 | ); | |
| c8f21c77 | 92 | |
| 07169b76 | 93 | // Make sure this doesn't fire until content_types is working, |
| 88741c1b KS |
94 | // and tables are updated, needed to avoid errors on initial installation. |
| 95 | if (!defined('MAINTENANCE_MODE') && variable_get('content_schema_version', -1) >= 6007) { | |
| 07169b76 KS |
96 | foreach (node_get_types() as $type) { |
| 97 | $type_name = $type->type; | |
| 169430ce | 98 | $content_type = content_types($type_name); |
| b4ad53bb | 99 | $type_url_str = $content_type['url_str']; |
| 04bf0773 | 100 | $items['admin/content/node-type/'. $type_url_str .'/fields'] = array( |
| b4ad53bb | 101 | 'title' => 'Manage fields', |
| 4a6e00e0 | 102 | 'page callback' => 'drupal_get_form', |
| 2c175854 | 103 | 'page arguments' => array('content_field_overview_form', $type_name), |
| b705d41a | 104 | 'access arguments' => array('administer content types'), |
| ddabf7ba | 105 | 'file' => 'includes/content.admin.inc', |
| 9ee39876 | 106 | 'type' => MENU_LOCAL_TASK, |
| e70aa81a | 107 | 'weight' => 1, |
| 4a6e00e0 | 108 | ); |
| 04bf0773 | 109 | $items['admin/content/node-type/'. $type_url_str .'/display'] = array( |
| b4ad53bb | 110 | 'title' => 'Display fields', |
| 4a6e00e0 | 111 | 'page callback' => 'drupal_get_form', |
| 2c175854 | 112 | 'page arguments' => array('content_display_overview_form', $type_name), |
| 4a6e00e0 | 113 | 'access arguments' => array('administer content types'), |
| ddabf7ba | 114 | 'file' => 'includes/content.admin.inc', |
| b4ad53bb | 115 | 'type' => MENU_LOCAL_TASK, |
| e70aa81a | 116 | 'weight' => 2, |
| e7bda925 | 117 | ); |
| a0f12ddb YC |
118 | $contexts = content_build_modes('_tabs'); |
| 119 | foreach ($contexts as $key => $tab) { | |
| 120 | $items['admin/content/node-type/'. $type_url_str .'/display/'. $key] = array( | |
| 121 | 'title' => $tab['title'], | |
| 122 | 'page arguments' => array('content_display_overview_form', $type_name, $key), | |
| 123 | 'access arguments' => array('administer content types'), | |
| 124 | 'type' => $key == 'basic' ? MENU_DEFAULT_LOCAL_TASK : MENU_LOCAL_TASK, | |
| 125 | 'weight' => $key == 'basic' ? 0 : 1, | |
| 126 | ); | |
| 127 | } | |
| 65b92acf KS |
128 | // Cast as an array in case this is called before any fields have |
| 129 | // been added, like when a new content type is created. | |
| 130 | foreach ((array) $content_type['fields'] as $field) { | |
| b61fd4af | 131 | $field_name = $field['field_name']; |
| 04bf0773 | 132 | $items['admin/content/node-type/'. $type_url_str .'/fields/'. $field_name] = array( |
| 126966c6 | 133 | 'title' => $field['widget']['label'], |
| b4ad53bb | 134 | 'page callback' => 'drupal_get_form', |
| 2c175854 | 135 | 'page arguments' => array('content_field_edit_form', $type_name, $field_name), |
| b4ad53bb | 136 | 'access arguments' => array('administer content types'), |
| ddabf7ba | 137 | 'file' => 'includes/content.admin.inc', |
| b5cb1b39 | 138 | 'type' => MENU_LOCAL_TASK, |
| b4ad53bb | 139 | ); |
| 04bf0773 | 140 | $items['admin/content/node-type/'. $type_url_str .'/fields/'. $field_name .'/remove'] = array( |
| b4ad53bb KS |
141 | 'title' => 'Remove field', |
| 142 | 'page callback' => 'drupal_get_form', | |
| 2c175854 | 143 | 'page arguments' => array('content_field_remove_form', $type_name, $field_name), |
| b4ad53bb | 144 | 'access arguments' => array('administer content types'), |
| ddabf7ba | 145 | 'file' => 'includes/content.admin.inc', |
| b4ad53bb KS |
146 | 'type' => MENU_CALLBACK, |
| 147 | ); | |
| 148 | } | |
| 9a7f744c JV |
149 | } |
| 150 | } | |
| 9a7f744c JV |
151 | return $items; |
| 152 | } | |
| 153 | ||
| 154 | /** | |
| 1fdfa6d9 YC |
155 | * Hook elements(). |
| 156 | * | |
| 157 | * Used to add multiple value processing, validation, and themes. | |
| 158 | * | |
| 159 | * FAPI callbacks can be declared here, and the element will be | |
| 160 | * passed to those callbacks. | |
| 161 | * | |
| 162 | * Drupal will automatically theme the element using a theme with | |
| 163 | * the same name as the hook_elements key. | |
| 164 | */ | |
| 165 | function content_elements() { | |
| 166 | return array( | |
| 167 | 'content_multiple_values' => array(), | |
| 168 | 'content_field' => array(), | |
| 169 | ); | |
| 170 | } | |
| 171 | ||
| 172 | /** | |
| b01b0973 KS |
173 | * Implementation of hook_theme(). |
| 174 | */ | |
| 175 | function content_theme() { | |
| 1948bbe4 | 176 | $path = drupal_get_path('module', 'content') .'/theme'; |
| ef664e13 | 177 | require_once "./$path/theme.inc"; |
| 169430ce | 178 | |
| b01b0973 | 179 | return array( |
| 9857a867 YC |
180 | 'content_field' => array( |
| 181 | 'template' => 'content-field', | |
| e74ada9a | 182 | 'arguments' => array('element' => NULL), |
| 1948bbe4 | 183 | 'path' => $path, |
| b705d41a | 184 | ), |
| 9f386144 YC |
185 | 'content_overview_links' => array( |
| 186 | 'arguments' => array(), | |
| 187 | ), | |
| 2c175854 | 188 | 'content_field_overview_form' => array( |
| ef664e13 YC |
189 | 'template' => 'content-admin-field-overview-form', |
| 190 | 'file' => 'theme.inc', | |
| 191 | 'path' => $path, | |
| 6e023c8c | 192 | 'arguments' => array('form' => NULL), |
| b705d41a | 193 | ), |
| 2c175854 | 194 | 'content_display_overview_form' => array( |
| ef664e13 YC |
195 | 'template' => 'content-admin-display-overview-form', |
| 196 | 'file' => 'theme.inc', | |
| 197 | 'path' => $path, | |
| 6e023c8c | 198 | 'arguments' => array('form' => NULL), |
| b705d41a | 199 | ), |
| 9363897f | 200 | 'content_exclude' => array( |
| 37916f9a KS |
201 | 'arguments' => array('content' => NULL, 'object' => array(), 'context' => NULL), |
| 202 | ), | |
| b01b0973 | 203 | 'content_view_multiple_field' => array( |
| 6e023c8c | 204 | 'arguments' => array('items' => NULL, 'field' => NULL, 'data' => NULL), |
| b705d41a | 205 | ), |
| e9b6b501 KS |
206 | 'content_multiple_values' => array( |
| 207 | 'arguments' => array('element' => NULL), | |
| 208 | ), | |
| b705d41a | 209 | ); |
| b01b0973 KS |
210 | } |
| 211 | ||
| 212 | /** | |
| d48f2c6d YC |
213 | * Implementation of hook_views_api(). |
| 214 | */ | |
| 215 | function content_views_api() { | |
| 216 | return array( | |
| 217 | 'api' => 2, | |
| 218 | 'path' => drupal_get_path('module', 'content') . '/includes/views', | |
| 219 | ); | |
| 220 | } | |
| 221 | ||
| 222 | /** | |
| aff1f800 YC |
223 | * Implementation of hook_ctools_plugin_directory(). |
| 224 | */ | |
| 225 | function content_ctools_plugin_directory($module, $plugin) { | |
| 226 | if ($module == 'ctools' && $plugin == 'content_types') { | |
| 227 | return 'includes/panels/' . $plugin; | |
| 228 | } | |
| 229 | } | |
| 230 | ||
| 231 | /** | |
| 124988a2 | 232 | * Load data for a node type's fields. |
| 64fb5eec | 233 | * Implementation of hook_nodeapi 'load' op. |
| 516bddc7 JC |
234 | * |
| 235 | * When loading one of the content.module nodes, we need to let each field handle | |
| 236 | * its own loading. This can make for a number of queries in some cases, so we | |
| 237 | * cache the loaded object structure and invalidate it during the update process. | |
| 9a7f744c | 238 | */ |
| b9d2cb37 | 239 | function content_load(&$node) { |
| 02305f18 | 240 | $cid = 'content:'. $node->nid .':'. $node->vid; |
| 6e0b21c2 | 241 | if ($cached = cache_get($cid, content_cache_tablename())) { |
| b9d2cb37 DP |
242 | foreach ($cached->data as $key => $value) { |
| 243 | $node->$key = $value; | |
| 244 | } | |
| 22b02ebf JC |
245 | } |
| 246 | else { | |
| 627ca7a4 JC |
247 | $default_additions = _content_field_invoke_default('load', $node); |
| 248 | if ($default_additions) { | |
| 249 | foreach ($default_additions as $key => $value) { | |
| 250 | $node->$key = $value; | |
| 251 | } | |
| 252 | } | |
| 22b02ebf | 253 | $additions = _content_field_invoke('load', $node); |
| 627ca7a4 JC |
254 | if ($additions) { |
| 255 | foreach ($additions as $key => $value) { | |
| f8a0f3eb | 256 | $node->$key = $value; |
| 627ca7a4 JC |
257 | $default_additions[$key] = $value; |
| 258 | } | |
| 259 | } | |
| 6e0b21c2 | 260 | cache_set($cid, $default_additions, content_cache_tablename()); |
| 22b02ebf | 261 | } |
| 9a7f744c JV |
262 | } |
| 263 | ||
| 264 | /** | |
| 64fb5eec | 265 | * Implementation of hook_nodeapi 'validate' op. |
| 84553847 KS |
266 | * |
| 267 | */ | |
| 64fb5eec | 268 | function content_validate(&$node, $form = NULL) { |
| 64fb5eec | 269 | _content_field_invoke_default('validate', $node, $form); |
| cb68e6f6 | 270 | _content_field_invoke('validate', $node, $form); |
| 84553847 KS |
271 | } |
| 272 | ||
| 273 | /** | |
| 64fb5eec | 274 | * Implementation of hook_nodeapi 'presave' op. |
| 84553847 KS |
275 | * |
| 276 | */ | |
| 277 | function content_presave(&$node) { | |
| 278 | _content_field_invoke('presave', $node); | |
| 279 | _content_field_invoke_default('presave', $node); | |
| 280 | } | |
| 281 | ||
| 282 | /** | |
| 64fb5eec | 283 | * Implementation of hook_nodeapi 'insert' op. |
| 74135a61 | 284 | * |
| 124988a2 | 285 | * Insert node type fields. |
| c70a734a JC |
286 | */ |
| 287 | function content_insert(&$node) { | |
| 288 | _content_field_invoke('insert', $node); | |
| 627ca7a4 | 289 | _content_field_invoke_default('insert', $node); |
| c70a734a JC |
290 | } |
| 291 | ||
| 292 | /** | |
| 64fb5eec | 293 | * Implementation of hook_nodeapi 'update' op. |
| 74135a61 | 294 | * |
| 124988a2 | 295 | * Update node type fields. |
| c70a734a JC |
296 | */ |
| 297 | function content_update(&$node) { | |
| 298 | _content_field_invoke('update', $node); | |
| 627ca7a4 | 299 | _content_field_invoke_default('update', $node); |
| 6e0b21c2 | 300 | cache_clear_all('content:'. $node->nid .':'. $node->vid, content_cache_tablename()); |
| c70a734a JC |
301 | } |
| 302 | ||
| 303 | /** | |
| 64fb5eec | 304 | * Implementation of hook_nodeapi 'delete' op. |
| 74135a61 | 305 | * |
| 124988a2 | 306 | * Delete node type fields. |
| c70a734a JC |
307 | */ |
| 308 | function content_delete(&$node) { | |
| 87c86cf8 KS |
309 | _content_field_invoke('delete', $node); |
| 310 | _content_field_invoke_default('delete', $node); | |
| 401cc03e | 311 | cache_clear_all('content:'. $node->nid .':', content_cache_tablename(), TRUE); |
| 124988a2 KS |
312 | } |
| 313 | ||
| 314 | /** | |
| 64fb5eec | 315 | * Implementation of hook_nodeapi 'delete_revision' op. |
| 74135a61 YC |
316 | * |
| 317 | * Delete node type fields for a revision. | |
| 124988a2 KS |
318 | */ |
| 319 | function content_delete_revision(&$node) { | |
| 87c86cf8 KS |
320 | _content_field_invoke('delete revision', $node); |
| 321 | _content_field_invoke_default('delete revision', $node); | |
| 6e0b21c2 | 322 | cache_clear_all('content:'. $node->nid .':'. $node->vid, content_cache_tablename()); |
| c70a734a JC |
323 | } |
| 324 | ||
| 325 | /** | |
| a8ea2012 | 326 | * Implementation of hook_nodeapi 'view' op. |
| 74135a61 | 327 | * |
| 124988a2 | 328 | * Generate field render arrays. |
| 9a7f744c JV |
329 | */ |
| 330 | function content_view(&$node, $teaser = FALSE, $page = FALSE) { | |
| e74ada9a YC |
331 | // Let field modules sanitize their data for output. |
| 332 | _content_field_invoke('sanitize', $node, $teaser, $page); | |
| 333 | ||
| edad4922 | 334 | // Merge fields. |
| 8731bc77 | 335 | $additions = _content_field_invoke_default('view', $node, $teaser, $page); |
| f8a0f3eb | 336 | $node->content = array_merge((array) $node->content, $additions); |
| 9a7f744c JV |
337 | } |
| 338 | ||
| 0c67ed2b | 339 | /** |
| a8ea2012 YC |
340 | * Render a single field, fully themed with label and multiple values. |
| 341 | * | |
| 342 | * To be used by third-party code (Views, Panels...) that needs to output | |
| 343 | * an isolated field. Do *not* use inside node templates, use the | |
| 344 | * $FIELD_NAME_rendered variables instead. | |
| 345 | * | |
| 346 | * By default, the field is displayed using the settings defined for the | |
| 347 | * 'full node' or 'teaser' contexts (depending on the value of the $teaser param). | |
| 348 | * Set $node->build_mode to a different value to use a different context. | |
| 349 | * | |
| 350 | * Different settings can be specified by adjusting $field['display_settings']. | |
| 351 | * | |
| 352 | * @param $field | |
| 353 | * The field definition. | |
| 354 | * @param $node | |
| 355 | * The node containing the field to display. Can be a 'pseudo-node', containing | |
| 356 | * at least 'type', 'nid', 'vid', and the field data. | |
| 357 | * @param $teaser | |
| 358 | * @param $page | |
| 359 | * Similar to hook_nodeapi('view') | |
| 360 | * @return | |
| 361 | * The themed output for the field. | |
| 362 | */ | |
| 363 | function content_view_field($field, $node, $teaser = FALSE, $page = FALSE) { | |
| 364 | $output = ''; | |
| 365 | if (isset($node->$field['field_name'])) { | |
| 366 | $items = $node->$field['field_name']; | |
| 367 | ||
| 368 | // Use 'full'/'teaser' if not specified otherwise. | |
| 369 | $node->build_mode = isset($node->build_mode) ? $node->build_mode : NODE_BUILD_NORMAL; | |
| 370 | ||
| 371 | // One-field equivalent to _content_field_invoke('sanitize'). | |
| 372 | $field_types = _content_field_types(); | |
| 373 | $module = $field_types[$field['type']]['module']; | |
| 374 | $function = $module .'_field'; | |
| 375 | if (function_exists($function)) { | |
| 376 | $function('sanitize', $node, $field, $items, $teaser, $page); | |
| 377 | $node->$field['field_name'] = $items; | |
| 378 | } | |
| 379 | ||
| 380 | $view = content_field('view', $node, $field, $items, $teaser, $page); | |
| 381 | // content_field('view') adds a wrapper to handle variables and 'excluded' | |
| 382 | // fields for node templates. We bypass it and render the actual field. | |
| 383 | $output = drupal_render($view[$field['field_name']]['field']); | |
| 384 | } | |
| 385 | return $output; | |
| 386 | } | |
| 387 | ||
| 388 | /** | |
| 64fb5eec | 389 | * Implementation of hook_nodeapi 'alter' op. |
| 71038871 YC |
390 | * |
| 391 | * Add back the formatted values in the 'view' element for all fields, | |
| 392 | * so that node templates can use it. | |
| 393 | */ | |
| 394 | function content_alter(&$node, $teaser = FALSE, $page = FALSE) { | |
| 395 | _content_field_invoke_default('alter', $node, $teaser, $page); | |
| 396 | } | |
| 397 | ||
| 398 | /** | |
| 64fb5eec | 399 | * Implementation of hook_nodeapi 'prepare translation' op. |
| 0c67ed2b YC |
400 | * |
| 401 | * Generate field render arrays. | |
| 402 | */ | |
| 07453112 | 403 | function content_prepare_translation(&$node) { |
| c9eaf362 YC |
404 | $default_additions = _content_field_invoke_default('prepare translation', $node); |
| 405 | $additions = _content_field_invoke('prepare translation', $node); | |
| a6ec9e19 YC |
406 | // Merge module additions after the default ones to enable overriding |
| 407 | // of field values. | |
| c9eaf362 | 408 | $node = (object) array_merge((array) $node, $default_additions, $additions); |
| 0c67ed2b | 409 | } |
| f8a0f3eb | 410 | |
| c70a734a | 411 | /** |
| 02305f18 | 412 | * Implementation of hook_nodeapi(). |
| 02305f18 | 413 | */ |
| 64fb5eec | 414 | function content_nodeapi(&$node, $op, $a3 = NULL, $a4 = NULL) { |
| a6ec9e19 | 415 | // Prevent against invalid 'nodes' built by broken 3rd party code. |
| a14dc251 YC |
416 | if (isset($node->type)) { |
| 417 | $type = content_types($node->type); | |
| a6ec9e19 YC |
418 | // Save cycles if the type has no CCK fields. |
| 419 | if (!empty($type['fields'])) { | |
| 420 | $callback = 'content_'. str_replace(' ', '_', $op); | |
| 421 | if (function_exists($callback)) { | |
| 422 | $callback($node, $a3, $a4); | |
| 423 | } | |
| 87c86cf8 | 424 | } |
| abfec8cd YC |
425 | |
| 426 | // Special case for 'view' op, we want to adjust weights of non-cck fields | |
| 427 | // even if there are no actual fields for this type. | |
| 428 | if ($op == 'view') { | |
| 12208d22 YC |
429 | $node->content['#pre_render'][] = 'content_alter_extra_weights'; |
| 430 | $node->content['#content_extra_fields'] = $type['extra']; | |
| abfec8cd | 431 | } |
| 124988a2 KS |
432 | } |
| 433 | } | |
| 434 | ||
| c32705eb KS |
435 | /** |
| 436 | * Implementation of hook_form_alter(). | |
| 437 | */ | |
| 4b9dd7c3 | 438 | function content_form_alter(&$form, $form_state, $form_id) { |
| 890b06de | 439 | if (isset($form['type']) && isset($form['#node']) && $form['type']['#value'] .'_node_form' == $form_id) { |
| 87c86cf8 KS |
440 | $type = content_types($form['#node']->type); |
| 441 | if (!empty($type['fields'])) { | |
| 442 | module_load_include('inc', 'content', 'includes/content.node_form'); | |
| 443 | // Merge field widgets. | |
| 444 | $form = array_merge($form, content_form($form, $form_state)); | |
| abfec8cd | 445 | } |
| 12208d22 YC |
446 | $form['#pre_render'][] = 'content_alter_extra_weights'; |
| 447 | $form['#content_extra_fields'] = $type['extra']; | |
| 448 | } | |
| 449 | } | |
| 450 | ||
| 451 | /** | |
| 452 | * Pre-render callback to adjust weights of non-CCK fields. | |
| 453 | */ | |
| 454 | function content_alter_extra_weights($elements) { | |
| 455 | if (isset($elements['#content_extra_fields'])) { | |
| 456 | foreach ($elements['#content_extra_fields'] as $key => $value) { | |
| 457 | // Some core 'fields' use a different key in node forms and in 'view' | |
| 458 | // render arrays. Check we're not on a form first. | |
| 459 | if (!isset($elements['#build_id']) && isset($value['view']) && isset($elements[$value['view']])) { | |
| 460 | $elements[$value['view']]['#weight'] = $value['weight']; | |
| 461 | } | |
| 462 | elseif (isset($elements[$key])) { | |
| 463 | $elements[$key]['#weight'] = $value['weight']; | |
| edad4922 | 464 | } |
| 124988a2 | 465 | } |
| 02305f18 | 466 | } |
| 12208d22 | 467 | return $elements; |
| c32705eb KS |
468 | } |
| 469 | ||
| 470 | /** | |
| 9372a36c YC |
471 | * Proxy function to call content_add_more_submit(), because it might not be |
| 472 | * included yet when the form is processed and invokes the callback. | |
| 473 | */ | |
| 474 | function content_add_more_submit_proxy($form, &$form_state) { | |
| 475 | module_load_include('inc', 'content', 'includes/content.node_form'); | |
| 476 | content_add_more_submit($form, $form_state); | |
| 477 | } | |
| 478 | ||
| 479 | /** | |
| cb68e6f6 MFM |
480 | * Proxy function to call content_multiple_value_after_build(), because it might |
| 481 | * not be included yet when the form is processed and invokes the callback. | |
| 482 | */ | |
| 483 | function content_multiple_value_after_build_proxy($elements, &$form_state) { | |
| 484 | module_load_include('inc', 'content', 'includes/content.node_form'); | |
| 485 | return content_multiple_value_after_build($elements, $form_state); | |
| 486 | } | |
| 487 | ||
| 488 | /** | |
| 41f2d6dc YC |
489 | * Theme an individual form element. |
| 490 | * | |
| 491 | * Combine multiple values into a table with drag-n-drop reordering. | |
| 492 | */ | |
| 493 | function theme_content_multiple_values($element) { | |
| 494 | $field_name = $element['#field_name']; | |
| 495 | $field = content_fields($field_name); | |
| 496 | $output = ''; | |
| 497 | ||
| 498 | if ($field['multiple'] >= 1) { | |
| 499 | $table_id = $element['#field_name'] .'_values'; | |
| 500 | $order_class = $element['#field_name'] .'-delta-order'; | |
| 3ed89d6e YC |
501 | $required = !empty($element['#required']) ? '<span class="form-required" title="'. t('This field is required.') .'">*</span>' : ''; |
| 502 | ||
| 41f2d6dc | 503 | $header = array( |
| 3ed89d6e | 504 | array( |
| eb0954b8 | 505 | 'data' => t('!title: !required', array('!title' => $element['#title'], '!required' => $required)), |
| 3ed89d6e YC |
506 | 'colspan' => 2 |
| 507 | ), | |
| cb68e6f6 | 508 | array('data' => t('Order'), 'class' => 'content-multiple-weight-header'), |
| 41f2d6dc | 509 | ); |
| cb68e6f6 MFM |
510 | if ($field['multiple'] == 1) { |
| 511 | $header[] = array('data' => '<span>'. t('Remove') .'</span>', 'class' => 'content-multiple-remove-header'); | |
| 512 | } | |
| 41f2d6dc YC |
513 | $rows = array(); |
| 514 | ||
| 98fd13df YC |
515 | // Sort items according to '_weight' (needed when the form comes back after |
| 516 | // preview or failed validation) | |
| 517 | $items = array(); | |
| 41f2d6dc YC |
518 | foreach (element_children($element) as $key) { |
| 519 | if ($key !== $element['#field_name'] .'_add_more') { | |
| cb68e6f6 | 520 | $items[$element[$key]['#delta']] = &$element[$key]; |
| 41f2d6dc YC |
521 | } |
| 522 | } | |
| cb68e6f6 | 523 | uasort($items, '_content_sort_items_value_helper'); |
| 98fd13df YC |
524 | |
| 525 | // Add the items as table rows. | |
| cb68e6f6 | 526 | foreach ($items as $delta => $item) { |
| 98fd13df YC |
527 | $item['_weight']['#attributes']['class'] = $order_class; |
| 528 | $delta_element = drupal_render($item['_weight']); | |
| cb68e6f6 MFM |
529 | if ($field['multiple'] == 1) { |
| 530 | $remove_element = drupal_render($item['_remove']); | |
| 531 | } | |
| 98fd13df YC |
532 | $cells = array( |
| 533 | array('data' => '', 'class' => 'content-multiple-drag'), | |
| 534 | drupal_render($item), | |
| 535 | array('data' => $delta_element, 'class' => 'delta-order'), | |
| 536 | ); | |
| cb68e6f6 MFM |
537 | $row_class = 'draggable'; |
| 538 | if ($field['multiple'] == 1) { | |
| 539 | if (!empty($item['_remove']['#default_value'])) { | |
| 540 | $row_class .= ' content-multiple-removed-row'; | |
| 541 | } | |
| 542 | $cells[] = array('data' => $remove_element, 'class' => 'content-multiple-remove-cell'); | |
| 543 | } | |
| 544 | $rows[] = array('data' => $cells, 'class' => $row_class); | |
| 98fd13df | 545 | } |
| 41f2d6dc YC |
546 | |
| 547 | $output .= theme('table', $header, $rows, array('id' => $table_id, 'class' => 'content-multiple-table')); | |
| 548 | $output .= $element['#description'] ? '<div class="description">'. $element['#description'] .'</div>' : ''; | |
| 549 | $output .= drupal_render($element[$element['#field_name'] .'_add_more']); | |
| 550 | ||
| 551 | drupal_add_tabledrag($table_id, 'order', 'sibling', $order_class); | |
| cb68e6f6 | 552 | drupal_add_js(drupal_get_path('module', 'content') .'/js/content.node_form.js'); |
| 41f2d6dc YC |
553 | } |
| 554 | else { | |
| 555 | foreach (element_children($element) as $key) { | |
| 556 | $output .= drupal_render($element[$key]); | |
| 557 | } | |
| 558 | } | |
| 559 | ||
| 560 | return $output; | |
| 561 | } | |
| 562 | ||
| 563 | /** | |
| 817e5dc6 KS |
564 | * Modules notify Content module when uninstalled, disabled, etc. |
| 565 | * | |
| 566 | * @param string $op | |
| 567 | * the module operation: uninstall, install, enable, disable | |
| 568 | * @param string $module | |
| 569 | * the name of the affected module. | |
| 570 | * @TODO | |
| 571 | * figure out exactly what needs to be done by content module when | |
| 572 | * field modules are installed, uninstalled, enabled or disabled. | |
| 573 | */ | |
| 574 | function content_notify($op, $module) { | |
| 817e5dc6 KS |
575 | switch ($op) { |
| 576 | case 'install': | |
| f7d071c1 | 577 | content_clear_type_cache(); |
| 817e5dc6 KS |
578 | break; |
| 579 | case 'uninstall': | |
| ddabf7ba | 580 | module_load_include('inc', 'content', 'includes/content.crud'); |
| 6e0b21c2 | 581 | content_module_delete($module); |
| 817e5dc6 KS |
582 | break; |
| 583 | case 'enable': | |
| cf81474b | 584 | content_associate_fields($module); |
| f7d071c1 | 585 | content_clear_type_cache(); |
| 817e5dc6 KS |
586 | break; |
| 587 | case 'disable': | |
| 88741c1b KS |
588 | // When CCK modules are disabled before content module's update is run |
| 589 | // to add the active column, we can't do this. | |
| e23be7d0 | 590 | if (variable_get('content_schema_version', -1) < 6007) { |
| ee6daf56 KS |
591 | return FALSE; |
| 592 | } | |
| c55c0a20 KS |
593 | db_query("UPDATE {". content_field_tablename() ."} SET active=0 WHERE module='%s'", $module); |
| 594 | db_query("UPDATE {". content_instance_tablename() ."} SET widget_active=0 WHERE widget_module='%s'", $module); | |
| f7d071c1 | 595 | content_clear_type_cache(TRUE); |
| 817e5dc6 KS |
596 | break; |
| 597 | } | |
| cf81474b KS |
598 | } |
| 599 | ||
| 600 | /** | |
| 52c35e0b YC |
601 | * Allows a module to update the database for fields and columns it controls. |
| 602 | * | |
| 603 | * @param string $module | |
| 604 | * The name of the module to update on. | |
| 605 | */ | |
| cf81474b | 606 | function content_associate_fields($module) { |
| ee6daf56 | 607 | // When CCK modules are enabled before content module's update is run, |
| 88741c1b | 608 | // to add module and active columns, we can't do this. |
| e23be7d0 | 609 | if (variable_get('content_schema_version', -1) < 6007) { |
| ee6daf56 KS |
610 | return FALSE; |
| 611 | } | |
| cf81474b KS |
612 | $module_fields = module_invoke($module, 'field_info'); |
| 613 | if ($module_fields) { | |
| 614 | foreach ($module_fields as $name => $field_info) { | |
| 52c35e0b | 615 | watchdog('content', 'Updating field type %type with module %module.', array('%type' => $name, '%module' => $module)); |
| a207fcc6 | 616 | db_query("UPDATE {". content_field_tablename() ."} SET module = '%s', active = %d WHERE type = '%s'", $module, 1, $name); |
| cf81474b KS |
617 | } |
| 618 | } | |
| 619 | $module_widgets = module_invoke($module, 'widget_info'); | |
| 620 | if ($module_widgets) { | |
| 621 | foreach ($module_widgets as $name => $widget_info) { | |
| 52c35e0b | 622 | watchdog('content', 'Updating widget type %type with module %module.', array('%type' => $name, '%module' => $module)); |
| a207fcc6 | 623 | db_query("UPDATE {". content_instance_tablename() ."} SET widget_module = '%s', widget_active = %d WHERE widget_type = '%s'", $module, 1, $name); |
| cf81474b KS |
624 | } |
| 625 | } | |
| 626 | // This is called from updates and installs, so get the install-safe | |
| 627 | // version of a fields array. | |
| cf81474b | 628 | $fields_set = array(); |
| d703adda | 629 | module_load_include('install', 'content'); |
| 4f059953 | 630 | $types = content_types_install(); |
| cf81474b KS |
631 | foreach ($types as $type_name => $fields) { |
| 632 | foreach ($fields as $field) { | |
| 633 | if ($field['module'] == $module && !in_array($field['field_name'], $fields_set)) { | |
| da76b44c | 634 | $columns = (array) module_invoke($field['module'], 'field_settings', 'database columns', $field); |
| 3ada1e96 | 635 | db_query("UPDATE {". content_field_tablename() ."} SET db_columns = '%s' WHERE field_name = '%s'", serialize($columns), $field['field_name']); |
| cf81474b KS |
636 | $fields_set[] = $field['field_name']; |
| 637 | } | |
| 638 | } | |
| 639 | } | |
| 817e5dc6 KS |
640 | } |
| 641 | ||
| 642 | /** | |
| c70a734a JC |
643 | * Implementation of hook_field(). Handles common field housekeeping. |
| 644 | * | |
| 645 | * This implementation is special, as content.module does not define any field | |
| 646 | * types. Instead, this function gets called after the type-specific hook, and | |
| e868d928 | 647 | * takes care of default stuff common to all field types. |
| 17a01567 YC |
648 | * |
| 649 | * Db-storage ops ('load', 'insert', 'update', 'delete', 'delete revisions') | |
| 650 | * are not executed field by field, and are thus handled separately in | |
| 651 | * content_storage. | |
| bc789494 KS |
652 | * |
| 653 | * The 'view' operation constructs the $node in a way that you can use | |
| 654 | * drupal_render() to display the formatted output for an individual field. | |
| 0c57bd61 | 655 | * i.e. print drupal_render($node->countent['field_foo']); |
| bc789494 KS |
656 | * |
| 657 | * The code now supports both single value formatters, which theme an | |
| 658 | * individual item value as has been done in previous version of CCK, | |
| 659 | * and multiple value formatters, which theme all values for the field | |
| 660 | * in a single theme. The multiple value formatters could be used, for | |
| 661 | * instance, to plot field values on a single map or display them | |
| 662 | * in a graph. Single value formatters are the default, multiple value | |
| 663 | * formatters can be designated as such in formatter_info(). | |
| 664 | * | |
| 665 | * The node array will look like: | |
| 0c57bd61 | 666 | * $node->content['field_foo']['wrapper'] = array( |
| 9857a867 | 667 | * '#type' => 'content_field', |
| bc789494 KS |
668 | * '#title' => 'label' |
| 669 | * '#field_name' => 'field_name', | |
| 670 | * '#node' => $node, | |
| fd6a6f8a YC |
671 | * // Value of the $teaser param of hook_nodeapi('view'). |
| 672 | * '#teaser' => $teaser, | |
| 673 | * // Value of the $page param of hook_nodeapi('view'). | |
| 674 | * '#page' => $page, | |
| 675 | * // The curent rendering context ('teaser', 'full', NODE_BUILD_SEARCH_INDEX...). | |
| 676 | * '#context' => $context, | |
| bc789494 KS |
677 | * 'items' => |
| 678 | * 0 => array( | |
| d703adda YC |
679 | * '#item' => $items[0], |
| 680 | * // Only for 'single-value' formatters | |
| bc789494 KS |
681 | * '#theme' => $theme, |
| 682 | * '#field_name' => 'field_name', | |
| 683 | * '#type_name' => $node->type, | |
| 684 | * '#formatter' => $formatter_name, | |
| 358cc3c0 KS |
685 | * '#node' => $node, |
| 686 | * '#delta' => 0, | |
| bc789494 KS |
687 | * ), |
| 688 | * 1 => array( | |
| d703adda YC |
689 | * '#item' => $items[1], |
| 690 | * // Only for 'single-value' formatters | |
| bc789494 KS |
691 | * '#theme' => $theme, |
| 692 | * '#field_name' => 'field_name', | |
| 693 | * '#type_name' => $node->type, | |
| 694 | * '#formatter' => $formatter_name, | |
| 358cc3c0 KS |
695 | * '#node' => $node, |
| 696 | * '#delta' => 1, | |
| bc789494 | 697 | * ), |
| d703adda | 698 | * // Only for 'multiple-value' formatters |
| bc789494 KS |
699 | * '#theme' => $theme, |
| 700 | * '#field_name' => 'field_name', | |
| 701 | * '#type_name' => $node->type, | |
| 702 | * '#formatter' => $formatter_name, | |
| bc789494 KS |
703 | * ), |
| 704 | * ); | |
| c70a734a | 705 | */ |
| 9a51ecbc | 706 | function content_field($op, &$node, $field, &$items, $teaser, $page) { |
| e868d928 YC |
707 | switch ($op) { |
| 708 | case 'validate': | |
| cb68e6f6 MFM |
709 | // If the field is configured for multiple values and these are handled |
| 710 | // by content module, we need to filter out items flagged for removal and | |
| 711 | // count non-empty items to enforce field requirement settings. | |
| 712 | if ($field['multiple'] >= 1 && content_handle('widget', 'multiple values', $field) == CONTENT_HANDLE_CORE) { | |
| 713 | module_load_include('inc', 'content', 'includes/content.node_form'); | |
| 714 | // Note that the $teaser argument for nodeapi('validate') is the $form. | |
| 715 | content_multiple_value_nodeapi_validate($node, $field, $items, $teaser); | |
| 716 | } | |
| e868d928 | 717 | break; |
| f8a0f3eb | 718 | |
| 9a51ecbc | 719 | case 'presave': |
| dd03cacd | 720 | if (!empty($node->devel_generate)) { |
| a6e65d38 | 721 | include_once('./'. drupal_get_path('module', 'content') .'/includes/content.devel.inc'); |
| 9f24adcc | 722 | content_generate_fields($node, $field); |
| 7d434244 | 723 | $items = $node->{$field['field_name']}; |
| a6e65d38 | 724 | } |
| 169430ce | 725 | |
| 78581ac6 YC |
726 | // Manual node_save calls might not have all fields filled in. |
| 727 | // On node insert, we need to make sure all tables get at least an empty | |
| 728 | // record, or subsequent edits, using drupal_write_record() in update mode, | |
| 729 | // won't insert any data. | |
| 730 | // Missing fields on node update are handled in content_storage(). | |
| 731 | if (empty($items) && !isset($node->nid)) { | |
| 732 | foreach (array_keys($field['columns']) as $column) { | |
| 733 | $items[0][$column] = NULL; | |
| 734 | } | |
| 735 | $node->$field['field_name'] = $items; | |
| 736 | } | |
| 737 | ||
| 5d8e26af | 738 | // If there was an AHAH add more button in this field, don't save it. |
| 3a2d35f5 | 739 | // TODO: is it still needed ? |
| 29e0b4ac | 740 | unset($items[$field['field_name'] .'_add_more']); |
| 5d8e26af | 741 | |
| 95247f8e | 742 | if (content_handle('widget', 'multiple values', $field) == CONTENT_HANDLE_CORE) { |
| e824dba6 KS |
743 | // Reorder items to account for drag-n-drop reordering. |
| 744 | $items = _content_sort_items($field, $items); | |
| 9a51ecbc | 745 | } |
| ab862123 | 746 | |
| cb68e6f6 | 747 | // Filter out items flagged for removal. |
| ab862123 YC |
748 | $items = content_set_empty($field, $items); |
| 749 | ||
| 9a51ecbc KS |
750 | break; |
| 751 | ||
| f8a0f3eb | 752 | case 'view': |
| 4c189214 | 753 | $addition = array(); |
| 1c137c5f | 754 | |
| cb68e6f6 MFM |
755 | // Previewed nodes bypass the 'presave' op, so we need to do some massaging. |
| 756 | if ($node->build_mode == NODE_BUILD_PREVIEW) { | |
| a167e7f2 YC |
757 | if (content_handle('widget', 'multiple values', $field) == CONTENT_HANDLE_CORE) { |
| 758 | // Reorder items to account for drag-n-drop reordering. | |
| 759 | $items = _content_sort_items($field, $items); | |
| 760 | } | |
| ab862123 | 761 | |
| cb68e6f6 | 762 | // Filter out items flagged for removal. |
| ab862123 | 763 | $items = content_set_empty($field, $items); |
| a167e7f2 YC |
764 | } |
| 765 | ||
| 5b9350a9 YC |
766 | // NODE_BUILD_NORMAL is 0, and ('whatever' == 0) is TRUE, so we need a ===. |
| 767 | if ($node->build_mode === NODE_BUILD_NORMAL || $node->build_mode == NODE_BUILD_PREVIEW) { | |
| f8a0f3eb YC |
768 | $context = $teaser ? 'teaser' : 'full'; |
| 769 | } | |
| 770 | else { | |
| 771 | $context = $node->build_mode; | |
| 772 | } | |
| 8dbf0d14 | 773 | // The field may be missing info for $contexts added by modules |
| 907eb38e KS |
774 | // enabled after the field was last edited. |
| 775 | $formatter_name = isset($field['display_settings'][$context]) && isset($field['display_settings'][$context]['format']) ? $field['display_settings'][$context]['format'] : 'default'; | |
| f56a59dd | 776 | if ($formatter = _content_get_formatter($formatter_name, $field['type'])) { |
| 29e0b4ac | 777 | $theme = $formatter['module'] .'_formatter_'. $formatter_name; |
| e833a6ee | 778 | $single = (content_handle('formatter', 'multiple values', $formatter) == CONTENT_HANDLE_CORE); |
| e74ada9a | 779 | |
| a8ea2012 YC |
780 | $label_display = isset($field['display_settings']['label']['format']) ? $field['display_settings']['label']['format'] : 'above'; |
| 781 | // Do not include field labels when indexing content. | |
| 782 | if ($context == NODE_BUILD_SEARCH_INDEX) { | |
| 783 | $label_display = 'hidden'; | |
| 784 | } | |
| 785 | ||
| e833a6ee | 786 | $element = array( |
| 9857a867 | 787 | '#type' => 'content_field', |
| eb0954b8 | 788 | '#title' => check_plain(t($field['widget']['label'])), |
| e833a6ee | 789 | '#field_name' => $field['field_name'], |
| c7d4ceea | 790 | '#access' => $formatter_name != 'hidden' && content_access('view', $field, NULL, $node), |
| a8ea2012 | 791 | '#label_display' => $label_display, |
| e833a6ee YC |
792 | '#node' => $node, |
| 793 | '#teaser' => $teaser, | |
| 794 | '#page' => $page, | |
| bd75d9c9 | 795 | '#context' => $context, |
| e833a6ee YC |
796 | '#single' => $single, |
| 797 | 'items' => array(), | |
| 798 | ); | |
| 799 | ||
| 800 | // Fill-in items. | |
| cb68e6f6 | 801 | foreach (array_keys($items) as $weight => $delta) { |
| e833a6ee | 802 | $element['items'][$delta] = array( |
| cb68e6f6 MFM |
803 | '#item' => $items[$delta], |
| 804 | '#weight' => $weight, | |
| e833a6ee YC |
805 | ); |
| 806 | } | |
| 807 | ||
| 808 | // Append formatter information either on each item ('single-value' formatter) | |
| 809 | // or at the upper 'items' level ('multiple-value' formatter) | |
| 810 | $format_info = array( | |
| 811 | '#theme' => $theme, | |
| 812 | '#field_name' => $field['field_name'], | |
| 813 | '#type_name' => $node->type, | |
| 814 | '#formatter' => $formatter_name, | |
| ce4a81c8 | 815 | '#node' => $node, |
| e833a6ee YC |
816 | ); |
| 817 | if ($single) { | |
| 818 | foreach ($items as $delta => $item) { | |
| 819 | $element['items'][$delta] += $format_info; | |
| 6d15ade9 | 820 | $element['items'][$delta]['#item']['#delta'] = $delta; |
| e833a6ee YC |
821 | } |
| 822 | } | |
| 823 | else { | |
| 824 | $element['items'] += $format_info; | |
| 825 | } | |
| 1c137c5f | 826 | |
| 1fdfa6d9 | 827 | // The wrapper lets us get the themed output for the whole field |
| 37916f9a KS |
828 | // to populate the $FIELD_NAME_rendered variable for node templates, |
| 829 | // and hide it from the $content variable if needed. | |
| 830 | // See 'preprocess_node' op and theme_content_field_wrapper()? | |
| 1fdfa6d9 YC |
831 | $wrapper = array( |
| 832 | 'field' => $element, | |
| 0c57bd61 | 833 | '#weight' => $field['widget']['weight'], |
| 37916f9a KS |
834 | '#post_render' => array('content_field_wrapper_post_render'), |
| 835 | '#field_name' => $field['field_name'], | |
| 836 | '#type_name' => $node->type, | |
| 837 | '#context' => $context, | |
| 1fdfa6d9 YC |
838 | ); |
| 839 | ||
| 840 | $addition = array($field['field_name'] => $wrapper); | |
| e833a6ee | 841 | } |
| 4c189214 | 842 | return $addition; |
| f8a0f3eb | 843 | |
| 5b9350a9 YC |
844 | case 'alter': |
| 845 | // Add back the formatted values in the 'view' element, | |
| 846 | // so that tokens and node templates can use it. | |
| 847 | // Note: Doing this in 'preprocess_node' breaks token integration. | |
| 1fdfa6d9 YC |
848 | |
| 849 | // The location of the field's rendered output depends on whether the | |
| 850 | // field is in a fieldgroup or not. | |
| bd37960b | 851 | $wrapper = NULL; |
| 71038871 | 852 | if (isset($node->content[$field['field_name']])) { |
| 1fdfa6d9 YC |
853 | $wrapper = $node->content[$field['field_name']]; |
| 854 | } | |
| bd37960b | 855 | elseif (module_exists('fieldgroup') && ($group_name = fieldgroup_get_group($node->type, $field['field_name'])) && isset($node->content[$group_name]['group'][$field['field_name']])) { |
| 1fdfa6d9 YC |
856 | $wrapper = $node->content[$group_name]['group'][$field['field_name']]; |
| 857 | } | |
| 858 | ||
| 859 | if ($wrapper) { | |
| 1fdfa6d9 YC |
860 | $element = $wrapper['field']; |
| 861 | // '#single' is not set if the field is hidden or inaccessible. | |
| 862 | if (isset($element['#single'])) { | |
| 863 | if (!empty($element['#single'])) { | |
| 864 | // Single value formatter. | |
| 865 | foreach (element_children($element['items']) as $delta) { | |
| 866 | // '#chilren' is not set if the field is empty. | |
| 867 | $items[$delta]['view'] = isset($element['items'][$delta]['#children']) ? $element['items'][$delta]['#children'] : ''; | |
| 868 | } | |
| 869 | } | |
| 870 | elseif (isset($element['items']['#children'])) { | |
| 871 | // Multiple values formatter. | |
| 872 | $items[0]['view'] = $element['items']['#children']; | |
| 71038871 YC |
873 | } |
| 874 | } | |
| 5b9350a9 YC |
875 | else { |
| 876 | // Hidden or inaccessible field. | |
| 877 | $items[0]['view'] = ''; | |
| 878 | } | |
| 879 | } | |
| 880 | break; | |
| 881 | ||
| 882 | case 'preprocess_node': | |
| 883 | // Add $FIELD_NAME_rendered variables. | |
| 884 | $addition = array(); | |
| 885 | ||
| 886 | // The location of the field's rendered output depends on whether the | |
| 887 | // field is in a fieldgroup or not. | |
| 888 | $wrapper = NULL; | |
| 889 | if (isset($node->content[$field['field_name']])) { | |
| 890 | $wrapper = $node->content[$field['field_name']]; | |
| 891 | } | |
| 892 | elseif (module_exists('fieldgroup') && ($group_name = fieldgroup_get_group($node->type, $field['field_name'])) && isset($node->content[$group_name]['group'][$field['field_name']])) { | |
| 893 | $wrapper = $node->content[$group_name]['group'][$field['field_name']]; | |
| 894 | } | |
| 895 | ||
| 896 | if ($wrapper) { | |
| 897 | // '#chilren' is not set if the field is empty. | |
| 898 | $addition[$field['field_name'] .'_rendered'] = isset($wrapper['#children']) ? $wrapper['#children'] : ''; | |
| 71038871 | 899 | } |
| 1fdfa6d9 YC |
900 | |
| 901 | return $addition; | |
| 71038871 | 902 | |
| 07453112 YC |
903 | case 'prepare translation': |
| 904 | $addition = array(); | |
| 905 | if (isset($node->translation_source->$field['field_name'])) { | |
| 906 | $addition[$field['field_name']] = $node->translation_source->$field['field_name']; | |
| 907 | } | |
| 0c67ed2b | 908 | return $addition; |
| e868d928 YC |
909 | } |
| 910 | } | |
| 0c67ed2b | 911 | |
| e868d928 | 912 | /** |
| cb68e6f6 | 913 | * Helper function to filter out items flagged for removal. |
| 77ee7e3b | 914 | * |
| ab862123 YC |
915 | * On order to keep marker rows in the database, the function ensures |
| 916 | * that the right number of 'all columns NULL' values is kept. | |
| 77ee7e3b KS |
917 | * |
| 918 | * @param array $field | |
| 919 | * @param array $items | |
| 920 | * @return array | |
| ab862123 | 921 | * returns filtered and adjusted item array |
| 77ee7e3b KS |
922 | */ |
| 923 | function content_set_empty($field, $items) { | |
| cb68e6f6 MFM |
924 | // Prepare an empty item. |
| 925 | $empty = array(); | |
| 926 | foreach (array_keys($field['columns']) as $column) { | |
| 927 | $empty[$column] = NULL; | |
| 928 | } | |
| 929 | ||
| 930 | // Filter out items flagged for removal. | |
| ab862123 | 931 | $filtered = array(); |
| 17a01567 | 932 | $function = $field['module'] .'_content_is_empty'; |
| 77ee7e3b | 933 | foreach ((array) $items as $delta => $item) { |
| cb68e6f6 MFM |
934 | if (empty($item['_remove'])) { |
| 935 | $filtered[] = ($function($item, $field) ? $empty : $item); | |
| 77ee7e3b KS |
936 | } |
| 937 | } | |
| ab862123 YC |
938 | |
| 939 | // Make sure we store the right number of 'empty' values. | |
| ab862123 YC |
940 | $pad = $field['multiple'] > 1 ? $field['multiple'] : 1; |
| 941 | $filtered = array_pad($filtered, $pad, $empty); | |
| 942 | ||
| 943 | return $filtered; | |
| 77ee7e3b KS |
944 | } |
| 945 | ||
| d703adda YC |
946 | /** |
| 947 | * Helper function to sort items in a field according to | |
| 948 | * user drag-n-drop reordering. | |
| 949 | */ | |
| e824dba6 KS |
950 | function _content_sort_items($field, $items) { |
| 951 | if ($field['multiple'] >= 1 && isset($items[0]['_weight'])) { | |
| 952 | usort($items, '_content_sort_items_helper'); | |
| edad4922 | 953 | foreach ($items as $delta => $item) { |
| cb68e6f6 | 954 | if (is_array($item) && isset($item['_weight'])) { |
| 880fb507 YC |
955 | unset($items[$delta]['_weight']); |
| 956 | } | |
| e824dba6 KS |
957 | } |
| 958 | } | |
| 959 | return $items; | |
| 960 | } | |
| 961 | ||
| 962 | /** | |
| d703adda YC |
963 | * Sort function for items order. |
| 964 | * (copied form element_sort(), which acts on #weight keys) | |
| e824dba6 KS |
965 | */ |
| 966 | function _content_sort_items_helper($a, $b) { | |
| c4dae5d3 YC |
967 | $a_weight = (is_array($a) && isset($a['_weight'])) ? $a['_weight'] : 0; |
| 968 | $b_weight = (is_array($b) && isset($b['_weight'])) ? $b['_weight'] : 0; | |
| 969 | if ($a_weight == $b_weight) { | |
| 970 | return 0; | |
| 971 | } | |
| 972 | return ($a_weight < $b_weight) ? -1 : 1; | |
| e824dba6 KS |
973 | } |
| 974 | ||
| 77ee7e3b | 975 | /** |
| 98fd13df YC |
976 | * Same as above, using ['_weight']['#value'] |
| 977 | */ | |
| 978 | function _content_sort_items_value_helper($a, $b) { | |
| 979 | $a_weight = (is_array($a) && isset($a['_weight']['#value'])) ? $a['_weight']['#value'] : 0; | |
| 980 | $b_weight = (is_array($b) && isset($b['_weight']['#value'])) ? $b['_weight']['#value'] : 0; | |
| 981 | if ($a_weight == $b_weight) { | |
| 982 | return 0; | |
| 983 | } | |
| 984 | return ($a_weight < $b_weight) ? -1 : 1; | |
| 985 | } | |
| 986 | ||
| 987 | /** | |
| 19cd3d92 | 988 | * Handle storage ops for _content_field_invoke_default(). |
| e868d928 YC |
989 | */ |
| 990 | function content_storage($op, $node) { | |
| 88741c1b KS |
991 | // Don't try this before content module's update is run to add |
| 992 | // the active and module columns. | |
| e23be7d0 | 993 | if (variable_get('content_schema_version', -1) < 6007) { |
| ee6daf56 KS |
994 | return FALSE; |
| 995 | } | |
| 996 | ||
| e868d928 YC |
997 | $type_name = $node->type; |
| 998 | $type = content_types($type_name); | |
| 169430ce | 999 | |
| c70a734a JC |
1000 | switch ($op) { |
| 1001 | case 'load': | |
| 3a2d35f5 YC |
1002 | // OPTIMIZE: load all non multiple fields in a single JOIN query ? |
| 1003 | // warning: 61-join limit in MySQL ? | |
| c6e53da7 | 1004 | $additions = array(); |
| e868d928 YC |
1005 | // For each table used by this content type, |
| 1006 | foreach ($type['tables'] as $table) { | |
| 1007 | $schema = drupal_get_schema($table); | |
| 19cd3d92 YC |
1008 | // The per-type table might not have any fields actually stored in it. |
| 1009 | if (!$schema['content fields']) { | |
| 1010 | continue; | |
| 1011 | } | |
| e868d928 YC |
1012 | $query = 'SELECT * FROM {'. $table .'} WHERE vid = %d'; |
| 1013 | ||
| 1014 | // If we're loading a table for a multiple field, | |
| 1015 | // we fetch all rows (values) ordered by delta, | |
| 1016 | // else we only fetch one row. | |
| 1017 | $result = isset($schema['fields']['delta']) ? db_query($query .' ORDER BY delta', $node->vid) : db_query_range($query, $node->vid, 0, 1); | |
| 1018 | ||
| 1019 | // For each table row, populate the fields. | |
| 1020 | while ($row = db_fetch_array($result)) { | |
| 1021 | // For each field stored in the table, add the field item. | |
| 1022 | foreach ($schema['content fields'] as $field_name) { | |
| 1023 | $item = array(); | |
| 1024 | $field = content_fields($field_name, $type_name); | |
| ea73d362 | 1025 | $db_info = content_database_info($field); |
| e868d928 | 1026 | // For each column declared by the field, populate the item. |
| ea73d362 | 1027 | foreach ($db_info['columns'] as $column => $attributes) { |
| e868d928 YC |
1028 | $item[$column] = $row[$attributes['column']]; |
| 1029 | } | |
| 1030 | ||
| 1031 | // Add the item to the field values for the node. | |
| 1032 | if (!isset($additions[$field_name])) { | |
| 1033 | $additions[$field_name] = array(); | |
| 1034 | } | |
| cb68e6f6 MFM |
1035 | |
| 1036 | // Preserve deltas when loading items from database. | |
| 1037 | if (isset($row['delta'])) { | |
| 1038 | $additions[$field_name][$row['delta']] = $item; | |
| 1039 | } | |
| 1040 | else { | |
| 1041 | $additions[$field_name][] = $item; | |
| 1042 | } | |
| e868d928 YC |
1043 | } |
| 1044 | } | |
| c70a734a JC |
1045 | } |
| 1046 | return $additions; | |
| 1047 | ||
| 340cf9c9 | 1048 | case 'insert': |
| 464a7920 | 1049 | case 'update': |
| e868d928 YC |
1050 | foreach ($type['tables'] as $table) { |
| 1051 | $schema = drupal_get_schema($table); | |
| 07453112 | 1052 | $record = array(); |
| e868d928 | 1053 | foreach ($schema['content fields'] as $field_name) { |
| 78581ac6 YC |
1054 | if (isset($node->$field_name)) { |
| 1055 | $field = content_fields($field_name, $type_name); | |
| 1056 | // Multiple fields need specific handling, we'll deal with them later on. | |
| 1057 | if ($field['multiple']) { | |
| 1058 | continue; | |
| 1059 | } | |
| 1060 | $db_info = content_database_info($field); | |
| 1061 | foreach ($db_info['columns'] as $column => $attributes) { | |
| 1062 | $record[$attributes['column']] = $node->{$field_name}[0][$column]; | |
| 1063 | } | |
| c70a734a | 1064 | } |
| 06728d7b | 1065 | } |
| 19cd3d92 YC |
1066 | // $record might be empty because |
| 1067 | // - the table stores a multiple field : | |
| 1068 | // we do nothing, this is handled later on | |
| 1069 | // - this is the per-type table and no field is actually stored in it : | |
| 1070 | // we still store the nid and vid | |
| 1071 | if (count($record) || empty($schema['content fields'])) { | |
| 07453112 YC |
1072 | $record['nid'] = $node->nid; |
| 1073 | $record['vid'] = $node->vid; | |
| 3870a5ea KS |
1074 | // Can't rely on the insert/update op of the node to decide if this |
| 1075 | // is an insert or an update, a node or revision may have existed | |
| 0314ae48 KS |
1076 | // before any fields were created, so there may not be an entry here. |
| 1077 | ||
| 1078 | // TODO - should we auto create an entry for all existing nodes when | |
| 1079 | // fields are added to content types -- either a NULL value | |
| 1080 | // or the default value? May need to offer the user an option of | |
| 1081 | // how to handle that. | |
| 3870a5ea | 1082 | if (db_result(db_query("SELECT COUNT(*) FROM {". $table ."} WHERE vid = %d", $node->vid))) { |
| 0bdbff4c | 1083 | content_write_record($table, $record, array('vid')); |
| 2f760ba1 KS |
1084 | } |
| 1085 | else { | |
| 0bdbff4c | 1086 | content_write_record($table, $record); |
| 2f760ba1 | 1087 | } |
| b7aa78d8 | 1088 | } |
| e868d928 | 1089 | } |
| b7aa78d8 | 1090 | |
| e868d928 YC |
1091 | // Handle multiple fields. |
| 1092 | foreach ($type['fields'] as $field) { | |
| 78581ac6 | 1093 | if ($field['multiple'] && isset($node->$field['field_name'])) { |
| e868d928 | 1094 | $db_info = content_database_info($field); |
| e868d928 YC |
1095 | // Delete and insert, rather than update, in case a value was added. |
| 1096 | if ($op == 'update') { | |
| 1097 | db_query('DELETE FROM {'. $db_info['table'] .'} WHERE vid = %d', $node->vid); | |
| b7aa78d8 | 1098 | } |
| cb68e6f6 MFM |
1099 | // Collect records for non-empty items. |
| 1100 | $function = $field['module'] .'_content_is_empty'; | |
| 1101 | $records = array(); | |
| e868d928 | 1102 | foreach ($node->$field['field_name'] as $delta => $item) { |
| cb68e6f6 MFM |
1103 | if (!$function($item, $field)) { |
| 1104 | $record = array(); | |
| 1105 | foreach ($db_info['columns'] as $column => $attributes) { | |
| 1106 | $record[$attributes['column']] = $item[$column]; | |
| 1107 | } | |
| 1108 | $record['nid'] = $node->nid; | |
| 1109 | $record['vid'] = $node->vid; | |
| 1110 | $record['delta'] = $delta; | |
| 1111 | $records[] = $record; | |
| 1112 | } | |
| 1113 | } | |
| 1114 | // If there was no non-empty item, insert delta 0 with NULL values. | |
| 1115 | if (empty($records)) { | |
| 07453112 | 1116 | $record = array(); |
| ea73d362 | 1117 | foreach ($db_info['columns'] as $column => $attributes) { |
| cb68e6f6 | 1118 | $record[$attributes['column']] = NULL; |
| e868d928 | 1119 | } |
| 07453112 YC |
1120 | $record['nid'] = $node->nid; |
| 1121 | $record['vid'] = $node->vid; | |
| cb68e6f6 MFM |
1122 | $record['delta'] = 0; |
| 1123 | $records[] = $record; | |
| 1124 | } | |
| 1125 | // Insert the collected records for this field into database. | |
| 1126 | foreach ($records as $record) { | |
| 0bdbff4c | 1127 | content_write_record($db_info['table'], $record); |
| b7aa78d8 | 1128 | } |
| c70a734a | 1129 | } |
| c70a734a | 1130 | } |
| e868d928 | 1131 | break; |
| c70a734a JC |
1132 | |
| 1133 | case 'delete': | |
| e868d928 YC |
1134 | foreach ($type['tables'] as $table) { |
| 1135 | db_query('DELETE FROM {'. $table .'} WHERE nid = %d', $node->nid); | |
| 1136 | } | |
| 1137 | break; | |
| 8ba1a662 JC |
1138 | |
| 1139 | case 'delete revision': | |
| e868d928 YC |
1140 | foreach ($type['tables'] as $table) { |
| 1141 | db_query('DELETE FROM {'. $table .'} WHERE vid = %d', $node->vid); | |
| 1142 | } | |
| 1143 | break; | |
| c70a734a JC |
1144 | } |
| 1145 | } | |
| 1146 | ||
| 9a7f744c | 1147 | /** |
| 0bdbff4c YC |
1148 | * Save a record to the database based upon the schema. |
| 1149 | * | |
| 1150 | * Directly copied from core's drupal_write_record, which can't update a | |
| 36acb2b7 | 1151 | * column to NULL. See http://drupal.org/node/227677 and |
| c86709d7 | 1152 | * http://drupal.org/node/226264 for more details about that problem. |
| 36acb2b7 YC |
1153 | * |
| 1154 | * TODO - get rid of this function and change references back to | |
| 9f381af8 KS |
1155 | * drupal_write_record() if the patch gets into core. Will need a method |
| 1156 | * of protecting people on older versions, though. | |
| 0bdbff4c YC |
1157 | * |
| 1158 | * Default values are filled in for missing items, and 'serial' (auto increment) | |
| 1159 | * types are filled in with IDs. | |
| 1160 | * | |
| 1161 | * @param $table | |
| 1162 | * The name of the table; this must exist in schema API. | |
| 1163 | * @param $object | |
| 1164 | * The object to write. This is a reference, as defaults according to | |
| 1165 | * the schema may be filled in on the object, as well as ID on the serial | |
| 1166 | * type(s). Both array an object types may be passed. | |
| 1167 | * @param $update | |
| 1168 | * If this is an update, specify the primary keys' field names. It is the | |
| 1169 | * caller's responsibility to know if a record for this object already | |
| 1170 | * exists in the database. If there is only 1 key, you may pass a simple string. | |
| 1171 | * @return | |
| 1172 | * Failure to write a record will return FALSE. Otherwise SAVED_NEW or | |
| 1173 | * SAVED_UPDATED is returned depending on the operation performed. The | |
| 1174 | * $object parameter contains values for any serial fields defined by | |
| 1175 | * the $table. For example, $object->nid will be populated after inserting | |
| 1176 | * a new node. | |
| 1177 | */ | |
| 1178 | function content_write_record($table, &$object, $update = array()) { | |
| 1179 | // Standardize $update to an array. | |
| 1180 | if (is_string($update)) { | |
| 1181 | $update = array($update); | |
| 1182 | } | |
| 1183 | ||
| 1184 | // Convert to an object if needed. | |
| 1185 | if (is_array($object)) { | |
| 1186 | $object = (object) $object; | |
| 1187 | $array = TRUE; | |
| 1188 | } | |
| 1189 | else { | |
| 1190 | $array = FALSE; | |
| 1191 | } | |
| 1192 | ||
| 1193 | $schema = drupal_get_schema($table); | |
| 1194 | if (empty($schema)) { | |
| 1195 | return FALSE; | |
| 1196 | } | |
| 1197 | ||
| 1198 | $fields = $defs = $values = $serials = $placeholders = array(); | |
| 1199 | ||
| 1200 | // Go through our schema, build SQL, and when inserting, fill in defaults for | |
| 1201 | // fields that are not set. | |
| 1202 | foreach ($schema['fields'] as $field => $info) { | |
| 1203 | // Special case -- skip serial types if we are updating. | |
| 1204 | if ($info['type'] == 'serial' && count($update)) { | |
| 1205 | continue; | |
| 1206 | } | |
| 1207 | ||
| 1208 | // For inserts, populate defaults from Schema if not already provided | |
| 1209 | if (!isset($object->$field) && !count($update) && isset($info['default'])) { | |
| 1210 | $object->$field = $info['default']; | |
| 1211 | } | |
| 1212 | ||
| 1213 | // Track serial fields so we can helpfully populate them after the query. | |
| 1214 | if ($info['type'] == 'serial') { | |
| 1215 | $serials[] = $field; | |
| 1216 | // Ignore values for serials when inserting data. Unsupported. | |
| 1217 | unset($object->$field); | |
| 1218 | } | |
| 1219 | ||
| 1220 | // Build arrays for the fields, placeholders, and values in our query. | |
| 1221 | if (isset($object->$field) || array_key_exists($field, $object)) { | |
| 1222 | $fields[] = $field; | |
| 1223 | if (isset($object->$field)) { | |
| 1224 | $placeholders[] = db_type_placeholder($info['type']); | |
| 1225 | ||
| 1226 | if (empty($info['serialize'])) { | |
| 1227 | $values[] = $object->$field; | |
| 1228 | } | |
| 1229 | else { | |
| 1230 | $values[] = serialize($object->$field); | |
| 1231 | } | |
| 1232 | } | |
| 1233 | else { | |
| 1234 | $placeholders[] = 'NULL'; | |
| 1235 | } | |
| 1236 | } | |
| 1237 | } | |
| 1238 | ||
| 1239 | // Build the SQL. | |
| 1240 | $query = ''; | |
| 1241 | if (!count($update)) { | |
| 1242 | $query = "INSERT INTO {". $table ."} (". implode(', ', $fields) .') VALUES ('. implode(', ', $placeholders) .')'; | |
| 1243 | $return = SAVED_NEW; | |
| 1244 | } | |
| 1245 | else { | |
| 1246 | $query = ''; | |
| 1247 | foreach ($fields as $id => $field) { | |
| 1248 | if ($query) { | |
| 1249 | $query .= ', '; | |
| 1250 | } | |
| 1251 | $query .= $field .' = '. $placeholders[$id]; | |
| 1252 | } | |
| 1253 | ||
| 64cb8552 | 1254 | foreach ($update as $key) { |
| 0bdbff4c YC |
1255 | $conditions[] = "$key = ". db_type_placeholder($schema['fields'][$key]['type']); |
| 1256 | $values[] = $object->$key; | |
| 1257 | } | |
| 1258 | ||
| 1259 | $query = "UPDATE {". $table ."} SET $query WHERE ". implode(' AND ', $conditions); | |
| 1260 | $return = SAVED_UPDATED; | |
| 1261 | } | |
| 1262 | ||
| 1263 | // Execute the SQL. | |
| 1264 | if (db_query($query, $values)) { | |
| 1265 | if ($serials) { | |
| 1266 | // Get last insert ids and fill them in. | |
| 1267 | foreach ($serials as $field) { | |
| 1268 | $object->$field = db_last_insert_id($table, $field); | |
| 1269 | } | |
| 1270 | } | |
| 1271 | ||
| 1272 | // If we began with an array, convert back so we don't surprise the caller. | |
| 1273 | if ($array) { | |
| 1274 | $object = (array) $object; | |
| 1275 | } | |
| 1276 | ||
| 1277 | return $return; | |
| 1278 | } | |
| 1279 | ||
| 1280 | return FALSE; | |
| 1281 | } | |
| 1282 | ||
| 1283 | /** | |
| d9367bd6 | 1284 | * Invoke a field hook. |
| c70a734a | 1285 | * |
| 627ca7a4 JC |
1286 | * For each operation, both this function and _content_field_invoke_default() are |
| 1287 | * called so that the default database handling can occur. | |
| 9a7f744c | 1288 | */ |
| 692e78f3 | 1289 | function _content_field_invoke($op, &$node, $teaser = NULL, $page = NULL) { |
| 9a7f744c | 1290 | $type_name = is_string($node) ? $node : (is_array($node) ? $node['type'] : $node->type); |
| b270a52c | 1291 | $type = content_types($type_name); |
| 9a7f744c JV |
1292 | $field_types = _content_field_types(); |
| 1293 | ||
| 1294 | $return = array(); | |
| 74889e02 YC |
1295 | foreach ($type['fields'] as $field) { |
| 1296 | $items = isset($node->$field['field_name']) ? $node->$field['field_name'] : array(); | |
| 1297 | ||
| 1298 | // Make sure AHAH 'add more' button isn't sent to the fields for processing. | |
| 1299 | unset($items[$field['field_name'] .'_add_more']); | |
| 1300 | ||
| 1301 | $module = $field_types[$field['type']]['module']; | |
| 1302 | $function = $module .'_field'; | |
| 1303 | if (function_exists($function)) { | |
| 1304 | $result = $function($op, $node, $field, $items, $teaser, $page); | |
| 1305 | if (is_array($result)) { | |
| 1306 | $return = array_merge($return, $result); | |
| 9a7f744c | 1307 | } |
| 74889e02 YC |
1308 | else if (isset($result)) { |
| 1309 | $return[] = $result; | |
| 9a7f744c JV |
1310 | } |
| 1311 | } | |
| 74889e02 YC |
1312 | // test for values in $items in case modules added items on insert |
| 1313 | if (isset($node->$field['field_name']) || count($items)) { | |
| 1314 | $node->$field['field_name'] = $items; | |
| 1315 | } | |
| 627ca7a4 | 1316 | } |
| 627ca7a4 JC |
1317 | return $return; |
| 1318 | } | |
| 1319 | ||
| 1320 | /** | |
| 1321 | * Invoke content.module's version of a field hook. | |
| 1322 | */ | |
| 1323 | function _content_field_invoke_default($op, &$node, $teaser = NULL, $page = NULL) { | |
| 1324 | $type_name = is_string($node) ? $node : (is_array($node) ? $node['type'] : $node->type); | |
| b270a52c | 1325 | $type = content_types($type_name); |
| c0e9b3b4 | 1326 | $field_types = _content_field_types(); |
| 627ca7a4 JC |
1327 | |
| 1328 | $return = array(); | |
| e868d928 YC |
1329 | // The operations involving database queries are better off handled by table |
| 1330 | // rather than by field. | |
| 29e0b4ac | 1331 | if (in_array($op, array('load', 'insert', 'update', 'delete', 'delete revision'))) { |
| e868d928 YC |
1332 | return content_storage($op, $node); |
| 1333 | } | |
| 74889e02 | 1334 | else { |
| 124988a2 | 1335 | foreach ($type['fields'] as $field) { |
| 6130c745 | 1336 | $items = isset($node->$field['field_name']) ? $node->$field['field_name'] : array(); |
| 4a1b60ae YC |
1337 | $result = content_field($op, $node, $field, $items, $teaser, $page); |
| 1338 | if (is_array($result)) { | |
| 1339 | $return = array_merge($return, $result); | |
| b7aa78d8 | 1340 | } |
| 4a1b60ae YC |
1341 | else if (isset($result)) { |
| 1342 | $return[] = $result; | |
| b7aa78d8 | 1343 | } |
| 335d6399 YC |
1344 | if (isset($node->$field['field_name'])) { |
| 1345 | $node->$field['field_name'] = $items; | |
| 1346 | } | |
| 4a1b60ae | 1347 | } |
| 9a7f744c | 1348 | } |
| 9a7f744c JV |
1349 | return $return; |
| 1350 | } | |
| 1351 | ||
| 9e301bfe | 1352 | /** |
| 9a7f744c | 1353 | * Return a list of all content types. |
| 516bddc7 | 1354 | * |
| 39f169c7 | 1355 | * @param $content_type_name |
| b270a52c | 1356 | * If set, return information on just this type. |
| 169430ce | 1357 | * |
| 5c9ca09d KS |
1358 | * Do some type checking and set up empty arrays for missing |
| 1359 | * info to avoid foreach errors elsewhere in the code. | |
| b270a52c | 1360 | */ |
| 124988a2 KS |
1361 | function content_types($type_name = NULL) { |
| 1362 | // handle type name with either an underscore or a dash | |
| 1363 | $type_name = !empty($type_name) ? str_replace('-', '_', $type_name) : NULL; | |
| 5f4b302a | 1364 | |
| b270a52c | 1365 | $info = _content_type_info(); |
| d799ee96 YC |
1366 | if (isset($info['content types'])) { |
| 1367 | if (!isset($type_name)) { | |
| 1368 | return $info['content types']; | |
| 1369 | } | |
| 124988a2 KS |
1370 | if (isset($info['content types'][$type_name])) { |
| 1371 | return $info['content types'][$type_name]; | |
| b270a52c | 1372 | } |
| b270a52c | 1373 | } |
| a14dc251 | 1374 | return array('tables' => array(), 'fields' => array(), 'extra' => array()); |
| b270a52c | 1375 | } |
| b270a52c JC |
1376 | |
| 1377 | /** | |
| 1378 | * Return a list of all fields. | |
| 39f169c7 JC |
1379 | * |
| 1380 | * @param $field_name | |
| 5430b849 | 1381 | * If not empty, return information on just this field. |
| 39f169c7 | 1382 | * @param $content_type_name |
| 5430b849 | 1383 | * If not empty, return information of the field within the context of this content |
| 39f169c7 | 1384 | * type. |
| 169430ce | 1385 | * |
| 5430b849 KS |
1386 | * Be sure to check empty() instead of isset() on field_name and |
| 1387 | * content_type_name to avoid bad results when the value is set | |
| 1388 | * but empty, as sometimes happens in the formatter. | |
| b270a52c | 1389 | */ |
| 39f169c7 | 1390 | function content_fields($field_name = NULL, $content_type_name = NULL) { |
| b270a52c | 1391 | $info = _content_type_info(); |
| d799ee96 | 1392 | if (isset($info['fields'])) { |
| 5430b849 | 1393 | if (empty($field_name)) { |
| d799ee96 YC |
1394 | return $info['fields']; |
| 1395 | } | |
| 39f169c7 | 1396 | if (isset($info['fields'][$field_name])) { |
| 5430b849 | 1397 | if (empty($content_type_name)) { |
| 39f169c7 JC |
1398 | return $info['fields'][$field_name]; |
| 1399 | } | |
| d799ee96 YC |
1400 | if (isset($info['content types'][$content_type_name]['fields'][$field_name])) { |
| 1401 | return $info['content types'][$content_type_name]['fields'][$field_name]; | |
| 1402 | } | |
| 39f169c7 JC |
1403 | } |
| 1404 | } | |
| b270a52c JC |
1405 | } |
| 1406 | ||
| 1407 | /** | |
| 1408 | * Return a list of field types. | |
| 1409 | */ | |
| 1410 | function _content_field_types() { | |
| 1411 | $info = _content_type_info(); | |
| b4ad53bb | 1412 | return isset($info['field types']) ? $info['field types'] : array(); |
| b270a52c JC |
1413 | } |
| 1414 | ||
| 1415 | /** | |
| 1416 | * Return a list of widget types. | |
| 1417 | */ | |
| 1418 | function _content_widget_types() { | |
| 1419 | $info = _content_type_info(); | |
| b4ad53bb | 1420 | return isset($info['widget types']) ? $info['widget types'] : array(); |
| b270a52c JC |
1421 | } |
| 1422 | ||
| 1423 | /** | |
| f56a59dd YC |
1424 | * Return the formatter description corresponding to a formatter name, |
| 1425 | * defaulting to 'default' if none is found. | |
| 1426 | */ | |
| 1427 | function _content_get_formatter($formatter_name, $field_type) { | |
| 1428 | $field_types = _content_field_types(); | |
| 1429 | $formatters = $field_types[$field_type]['formatters']; | |
| 1430 | ||
| 1431 | if (!isset($formatters[$formatter_name]) && $formatter_name != 'hidden') { | |
| 1432 | // This might happen when the selected formatter has been renamed in the | |
| 1433 | // module, or if the module has been disabled since then. | |
| 1434 | $formatter_name = 'default'; | |
| 1435 | } | |
| 1436 | ||
| 1437 | return isset($formatters[$formatter_name]) ? $formatters[$formatter_name] : FALSE; | |
| 1438 | } | |
| 1439 | ||
| 1440 | /** | |
| b270a52c JC |
1441 | * Collate all information on content types, fields, and related structures. |
| 1442 | * | |
| 516bddc7 JC |
1443 | * @param $reset |
| 1444 | * If TRUE, clear the cache and fetch the information from the database again. | |
| 9a7f744c | 1445 | */ |
| b270a52c | 1446 | function _content_type_info($reset = FALSE) { |
| 395238e1 | 1447 | global $language; |
| 5a57e5e1 | 1448 | static $info; |
| b4ad53bb | 1449 | |
| 5a57e5e1 | 1450 | if ($reset || !isset($info)) { |
| 870a7dc2 | 1451 | // Make sure this function doesn't run until the tables have been created, |
| 55000f41 YC |
1452 | // For instance: when first enabled and called from content_menu(), |
| 1453 | // or when uninstalled and some subsequent field module uninstall | |
| 1454 | // attempts to refresh the data. | |
| ee6daf56 | 1455 | |
| 88741c1b KS |
1456 | // Don't try this before content module's update is run |
| 1457 | // to add module and active columns to the table. | |
| e23be7d0 | 1458 | if (variable_get('content_schema_version', -1) < 6007) { |
| 55000f41 | 1459 | return array(); |
| 870a7dc2 | 1460 | } |
| edad4922 | 1461 | |
| 395238e1 | 1462 | if (!$reset && $cached = cache_get('content_type_info:'. $language->language, content_cache_tablename())) { |
| 5a57e5e1 | 1463 | $info = $cached->data; |
| 9a7f744c JV |
1464 | } |
| 1465 | else { | |
| b270a52c JC |
1466 | $info = array( |
| 1467 | 'field types' => array(), | |
| 1468 | 'widget types' => array(), | |
| 1469 | 'fields' => array(), | |
| 1470 | 'content types' => array(), | |
| 1471 | ); | |
| 1472 | ||
| 83e2967d | 1473 | // Populate field types. |
| b270a52c | 1474 | foreach (module_list() as $module) { |
| a5dc9b5d KS |
1475 | $module_field_types = module_invoke($module, 'field_info'); |
| 1476 | if ($module_field_types) { | |
| 1477 | foreach ($module_field_types as $name => $field_info) { | |
| 411876a9 KS |
1478 | // Truncate names to match the value that is stored in the database. |
| 1479 | $db_name = substr($name, 0, 32); | |
| 1480 | $info['field types'][$db_name] = $field_info; | |
| 1481 | $info['field types'][$db_name]['module'] = $module; | |
| 1482 | $info['field types'][$db_name]['formatters'] = array(); | |
| b270a52c JC |
1483 | } |
| 1484 | } | |
| 83e2967d | 1485 | } |
| 411876a9 | 1486 | |
| 83e2967d YC |
1487 | // Populate widget types and formatters for known field types. |
| 1488 | foreach (module_list() as $module) { | |
| 1489 | if ($module_widgets = module_invoke($module, 'widget_info')) { | |
| a5dc9b5d | 1490 | foreach ($module_widgets as $name => $widget_info) { |
| 411876a9 KS |
1491 | // Truncate names to match the value that is stored in the database. |
| 1492 | $db_name = substr($name, 0, 32); | |
| 1493 | $info['widget types'][$db_name] = $widget_info; | |
| 1494 | $info['widget types'][$db_name]['module'] = $module; | |
| 83e2967d YC |
1495 | // Replace field types with db_compatible version of known field types. |
| 1496 | $info['widget types'][$db_name]['field types'] = array(); | |
| 1497 | foreach ($widget_info['field types'] as $field_type) { | |
| 1498 | $field_type_db_name = substr($field_type, 0, 32); | |
| 1499 | if (isset($info['field types'][$field_type_db_name])) { | |
| 7e132f14 | 1500 | $info['widget types'][$db_name]['field types'][] = $field_type_db_name; |
| 83e2967d | 1501 | } |
| 411876a9 | 1502 | } |
| b270a52c JC |
1503 | } |
| 1504 | } | |
| 06b78b01 | 1505 | |
| 83e2967d | 1506 | if ($module_formatters = module_invoke($module, 'field_formatter_info')) { |
| c7c1c5d9 YC |
1507 | foreach ($module_formatters as $name => $formatter_info) { |
| 1508 | foreach ($formatter_info['field types'] as $field_type) { | |
| 411876a9 KS |
1509 | // Truncate names to match the value that is stored in the database. |
| 1510 | $db_name = substr($field_type, 0, 32); | |
| 83e2967d YC |
1511 | if (isset($info['field types'][$db_name])) { |
| 1512 | $info['field types'][$db_name]['formatters'][$name] = $formatter_info; | |
| 1513 | $info['field types'][$db_name]['formatters'][$name]['module'] = $module; | |
| 1514 | } | |
| c7c1c5d9 YC |
1515 | } |
| 1516 | } | |
| a5dc9b5d | 1517 | } |
| b270a52c | 1518 | } |
| 411876a9 | 1519 | |
| 83e2967d | 1520 | // Populate actual field instances. |
| ddabf7ba | 1521 | module_load_include('inc', 'content', 'includes/content.crud'); |
| adbcbef4 | 1522 | foreach (node_get_types('types', NULL, TRUE) as $type_name => $data) { |
| b4ad53bb | 1523 | $type = (array) $data; |
| 124988a2 | 1524 | $type['url_str'] = str_replace('_', '-', $type['type']); |
| e62d0eb9 | 1525 | $type['fields'] = array(); |
| f42b9bcb | 1526 | $type['tables'] = array(); |
| 19cd3d92 YC |
1527 | if ($fields = content_field_instance_read(array('type_name' => $type_name))) { |
| 1528 | foreach ($fields as $field) { | |
| 19cd3d92 YC |
1529 | $db_info = content_database_info($field); |
| 1530 | $type['tables'][$db_info['table']] = $db_info['table']; | |
| 395238e1 MFM |
1531 | |
| 1532 | // Allow external modules to translate field strings. | |
| 1533 | $field_strings = array( | |
| 1534 | 'widget_label' => $field['widget']['label'], | |
| 1535 | 'widget_description' => $field['widget']['description'], | |
| 1536 | ); | |
| f79d3d69 | 1537 | drupal_alter('content_field_strings', $field_strings, $field['type_name'], $field['field_name']); |
| 395238e1 MFM |
1538 | $field['widget']['label'] = $field_strings['widget_label']; |
| 1539 | $field['widget']['description'] = $field_strings['widget_description']; | |
| 1540 | ||
| f79d3d69 | 1541 | $type['fields'][$field['field_name']] = $field; |
| 19cd3d92 YC |
1542 | // This means that content_fields($field_name) (no type name) |
| 1543 | // returns the last instance loaded. | |
| 1544 | $info['fields'][$field['field_name']] = $field; | |
| 1545 | } | |
| 1546 | // Make sure the per-type table is added, even if no field is actually | |
| 1547 | // stored in it. | |
| 1548 | $table = _content_tablename($type['type'], CONTENT_DB_STORAGE_PER_CONTENT_TYPE); | |
| 1549 | $type['tables'][$table] = $table; | |
| 9a7f744c | 1550 | } |
| edad4922 | 1551 | |
| 2e55329a | 1552 | // Gather information about non-CCK 'fields'. |
| edad4922 YC |
1553 | $extra = module_invoke_all('content_extra_fields', $type_name); |
| 1554 | drupal_alter('content_extra_fields', $extra, $type_name); | |
| 1555 | // Add saved weights. | |
| 1556 | foreach (variable_get('content_extra_weights_'. $type_name, array()) as $key => $value) { | |
| 1557 | // Some stored entries might not exist anymore, for instance if uploads | |
| 1558 | // have been disabled, or vocabularies removed... | |
| 1559 | if (isset($extra[$key])) { | |
| 1560 | $extra[$key]['weight'] = $value; | |
| 1561 | } | |
| 1562 | } | |
| 1563 | $type['extra'] = $extra; | |
| 1564 | ||
| b4ad53bb | 1565 | $info['content types'][$type_name] = $type; |
| 9a7f744c | 1566 | } |
| edad4922 | 1567 | |
| 395238e1 | 1568 | cache_set('content_type_info:'. $language->language, $info, content_cache_tablename()); |
| 9a7f744c JV |
1569 | } |
| 1570 | } | |
| 5a57e5e1 | 1571 | return $info; |
| 1182d802 JC |
1572 | } |
| 1573 | ||
| 1574 | /** | |
| 77f9b7e9 KS |
1575 | * Implementation of hook_node_type() |
| 1576 | * React to change in node types | |
| 1577 | */ | |
| 1578 | function content_node_type($op, $info) { | |
| 1579 | switch ($op) { | |
| c7c1c5d9 | 1580 | case 'insert': |
| ddabf7ba | 1581 | module_load_include('inc', 'content', 'includes/content.crud'); |
| 77f9b7e9 KS |
1582 | content_type_create($info); |
| 1583 | break; | |
| c7c1c5d9 | 1584 | case 'update': |
| ddabf7ba | 1585 | module_load_include('inc', 'content', 'includes/content.crud'); |
| 77f9b7e9 KS |
1586 | content_type_update($info); |
| 1587 | break; | |
| c7c1c5d9 | 1588 | case 'delete': |
| ddabf7ba | 1589 | module_load_include('inc', 'content', 'includes/content.crud'); |
| 77f9b7e9 KS |
1590 | content_type_delete($info); |
| 1591 | break; | |
| 1592 | } | |
| 1593 | } | |
| 1594 | ||
| 1595 | /** | |
| 516bddc7 JC |
1596 | * Clear the cache of content_types; called in several places when content |
| 1597 | * information is changed. | |
| dbc05900 | 1598 | */ |
| 17a01567 | 1599 | function content_clear_type_cache($rebuild_schema = FALSE) { |
| 2edef5e0 | 1600 | cache_clear_all('*', content_cache_tablename(), TRUE); |
| b270a52c | 1601 | _content_type_info(TRUE); |
| c70a734a | 1602 | |
| cfff5104 | 1603 | // Refresh the schema to pick up new information. |
| 17a01567 | 1604 | if ($rebuild_schema) { |
| 1e5dc3e9 | 1605 | $schema = drupal_get_schema(NULL, TRUE); |
| cfff5104 KS |
1606 | } |
| 1607 | ||
| f53a9d94 KS |
1608 | if (module_exists('views')) { |
| 1609 | // Needed because this can be called from .install files | |
| 1610 | module_load_include('module', 'views'); | |
| 1611 | views_invalidate_cache(); | |
| 1612 | } | |
| dbc05900 | 1613 | } |
| c1f81293 | 1614 | |
| 4af0dd2f | 1615 | /** |
| 463539db JC |
1616 | * Retrieve the database storage location(s) for a field. |
| 1617 | * | |
| 3a2d35f5 | 1618 | * TODO: add a word about why it's not included in the global _content_type_info array. |
| e868d928 | 1619 | * |
| 463539db JC |
1620 | * @param $field |
| 1621 | * The field whose database information is requested. | |
| 1622 | * @return | |
| 1623 | * An array with the keys: | |
| 1624 | * "table": The name of the database table where the field data is stored. | |
| 1625 | * "columns": An array of columns stored for this field. Each is a collection | |
| 1626 | * of information returned from hook_field_settings('database columns'), | |
| 1627 | * with the addition of a "column" attribute which holds the name of the | |
| 1628 | * database column that stores the data. | |
| 1629 | */ | |
| 1630 | function content_database_info($field) { | |
| 463539db | 1631 | $db_info = array(); |
| c46b3c82 | 1632 | if ($field['db_storage'] == CONTENT_DB_STORAGE_PER_FIELD) { |
| 6fa59346 | 1633 | $db_info['table'] = _content_tablename($field['field_name'], CONTENT_DB_STORAGE_PER_FIELD); |
| c46b3c82 JC |
1634 | } |
| 1635 | else { | |
| 6fa59346 | 1636 | $db_info['table'] = _content_tablename($field['type_name'], CONTENT_DB_STORAGE_PER_CONTENT_TYPE); |
| c46b3c82 | 1637 | } |
| 463539db | 1638 | |
| d799ee96 YC |
1639 | $db_info['columns'] = (array) $field['columns']; |
| 1640 | // Generate column names for this field from generic column names. | |
| 1641 | foreach ($db_info['columns'] as $column_name => $attributes) { | |
| 1642 | $db_info['columns'][$column_name]['column'] = $field['field_name'] .'_'. $column_name; | |
| 463539db | 1643 | } |
| d799ee96 | 1644 | |
| 463539db JC |
1645 | return $db_info; |
| 1646 | } | |
| 1647 | ||
| 1648 | /** | |
| 77ee7e3b KS |
1649 | * Helper function for identifying the storage type for a field. |
| 1650 | */ | |
| 1651 | function content_storage_type($field) { | |
| 1652 | if ($field['multiple'] > 0) { | |
| 1653 | return CONTENT_DB_STORAGE_PER_FIELD; | |
| 1654 | } | |
| 1655 | else { | |
| ddabf7ba | 1656 | module_load_include('inc', 'content', 'includes/content.crud'); |
| 77ee7e3b | 1657 | $instances = content_field_instance_read(array('field_name' => $field['field_name'])); |
| a51c59f1 | 1658 | if (count($instances) > 1) { |
| 77ee7e3b KS |
1659 | return CONTENT_DB_STORAGE_PER_FIELD; |
| 1660 | } | |
| 77ee7e3b | 1661 | } |
| 2e55329a | 1662 | return CONTENT_DB_STORAGE_PER_CONTENT_TYPE; |
| 77ee7e3b KS |
1663 | } |
| 1664 | ||
| 1665 | /** | |
| 4af0dd2f JC |
1666 | * Manipulate a 2D array to reverse rows and columns. |
| 1667 | * | |
| 06728d7b JC |
1668 | * The default data storage for fields is delta first, column names second. |
| 1669 | * This is sometimes inconvenient for field modules, so this function can be | |
| 1670 | * used to present the data in an alternate format. | |
| 4af0dd2f JC |
1671 | * |
| 1672 | * @param $array | |
| 1673 | * The array to be transposed. It must be at least two-dimensional, and | |
| 1674 | * the subarrays must all have the same keys or behavior is undefined. | |
| 4af0dd2f JC |
1675 | * @return |
| 1676 | * The transposed array. | |
| 1677 | */ | |
| 1678 | function content_transpose_array_rows_cols($array) { | |
| 1679 | $result = array(); | |
| 07dd6b84 JC |
1680 | if (is_array($array)) { |
| 1681 | foreach ($array as $key1 => $value1) { | |
| 1682 | if (is_array($value1)) { | |
| 1683 | foreach ($value1 as $key2 => $value2) { | |
| 1684 | if (!isset($result[$key2])) { | |
| 1685 | $result[$key2] = array(); | |
| 1686 | } | |
| 1687 | $result[$key2][$key1] = $value2; | |
| 1688 | } | |
| 4af0dd2f | 1689 | } |
| 4af0dd2f JC |
1690 | } |
| 1691 | } | |
| 1692 | return $result; | |
| 1693 | } | |
| 1694 | ||
| 55f0c252 | 1695 | /** |
| 1459e971 MFM |
1696 | * Helper function to flatten an array of allowed values. |
| 1697 | * | |
| 1698 | * @param $array | |
| 1699 | * A single or multidimensional array. | |
| 1700 | * @return | |
| 1701 | * A flattened array. | |
| 1702 | */ | |
| 1703 | function content_array_flatten($array) { | |
| 1704 | $result = array(); | |
| 1705 | if (is_array($array)) { | |
| 1706 | foreach ($array as $key => $value) { | |
| 1707 | if (is_array($value)) { | |
| 1708 | $result += content_array_flatten($value); | |
| 1709 | } | |
| 1710 | else { | |
| 1711 | $result[$key] = $value; | |
| 1712 | } | |
| 1713 | } | |
| 1714 | } | |
| 1715 | return $result; | |
| 1716 | } | |
| 1717 | ||
| 1718 | /** | |
| e9b6b501 KS |
1719 | * Create an array of the allowed values for this field. |
| 1720 | * | |
| 1721 | * Used by number and text fields, expects to find either | |
| 156714f4 | 1722 | * PHP code that will return the correct value, or a string |
| e9b6b501 KS |
1723 | * with keys and labels separated with '|' and with each |
| 1724 | * new value on its own line. | |
| 1459e971 MFM |
1725 | * |
| 1726 | * @param $field | |
| 1727 | * The field whose allowed values are requested. | |
| 1728 | * @param $flatten | |
| 1729 | * Optional. Use TRUE to return a flattened array (default). | |
| 1730 | * FALSE can be used to support optgroups for select widgets | |
| 1731 | * when allowed values list is generated using PHP code. | |
| e9b6b501 | 1732 | */ |
| 1459e971 | 1733 | function content_allowed_values($field, $flatten = TRUE) { |
| e9b6b501 KS |
1734 | static $allowed_values; |
| 1735 | ||
| 1459e971 MFM |
1736 | $cid = $field['field_name'] .':'. ($flatten ? '1' : '0'); |
| 1737 | if (isset($allowed_values[$cid])) { | |
| 1738 | return $allowed_values[$cid]; | |
| e9b6b501 KS |
1739 | } |
| 1740 | ||
| 1459e971 | 1741 | $allowed_values[$cid] = array(); |
| e9b6b501 KS |
1742 | |
| 1743 | if (isset($field['allowed_values_php'])) { | |
| 1744 | ob_start(); | |
| 1745 | $result = eval($field['allowed_values_php']); | |
| 1746 | if (is_array($result)) { | |
| 1459e971 MFM |
1747 | if ($flatten) { |
| 1748 | $result = content_array_flatten($result); | |
| 1749 | } | |
| 1750 | $allowed_values[$cid] = $result; | |
| e9b6b501 KS |
1751 | } |
| 1752 | ob_end_clean(); | |
| 1753 | } | |
| 1754 | ||
| 1459e971 | 1755 | if (empty($allowed_values[$cid]) && isset($field['allowed_values'])) { |
| e9b6b501 KS |
1756 | $list = explode("\n", $field['allowed_values']); |
| 1757 | $list = array_map('trim', $list); | |
| 1758 | $list = array_filter($list, 'strlen'); | |
| 1759 | foreach ($list as $opt) { | |
| eb0954b8 YC |
1760 | // Sanitize the user input with a permissive filter. |
| 1761 | $opt = content_filter_xss($opt); | |
| e9b6b501 KS |
1762 | if (strpos($opt, '|') !== FALSE) { |
| 1763 | list($key, $value) = explode('|', $opt); | |
| 1459e971 | 1764 | $allowed_values[$cid][$key] = (isset($value) && $value !=='') ? $value : $key; |
| e9b6b501 KS |
1765 | } |
| 1766 | else { | |
| 1459e971 | 1767 | $allowed_values[$cid][$opt] = $opt; |
| e9b6b501 KS |
1768 | } |
| 1769 | } | |
| 395238e1 | 1770 | // Allow external modules to translate allowed values list. |
| 1459e971 | 1771 | drupal_alter('content_allowed_values', $allowed_values[$cid], $field); |
| e9b6b501 | 1772 | } |
| 1459e971 | 1773 | return $allowed_values[$cid]; |
| e9b6b501 KS |
1774 | } |
| 1775 | ||
| 1776 | /** | |
| 8435da94 MFM |
1777 | * Filter out HTML from allowed values array while leaving entities unencoded. |
| 1778 | * | |
| 1779 | * @see content_allowed_values() | |
| 1780 | * @see optionwidgets_select_process() | |
| 1781 | * @see content_handler_filter_many_to_one::allowed_values() | |
| 1782 | */ | |
| 1783 | function content_allowed_values_filter_html(&$options) { | |
| 1784 | foreach ($options as $key => $opt) { | |
| 1785 | if (is_array($opt)) { | |
| 1786 | content_allowed_values_filter_html($options[$key]); | |
| 1787 | } | |
| 1788 | else { | |
| 1789 | $options[$key] = html_entity_decode(strip_tags($opt), ENT_QUOTES); | |
| 1790 | } | |
| 1791 | } | |
| 1792 | } | |
| 1793 | ||
| 1794 | /** | |
| eb0954b8 YC |
1795 | * Like filter_xss_admin(), but with a shorter list of allowed tags. |
| 1796 | * | |
| 1797 | * Used for items entered by administrators, like field descriptions, | |
| 1798 | * allowed values, where some (mainly inline) mark-up may be desired | |
| 1799 | * (so check_plain() is not acceptable). | |
| 1800 | */ | |
| 1801 | function content_filter_xss($string) { | |
| 1802 | return filter_xss($string, _content_filter_xss_allowed_tags()); | |
| 1803 | } | |
| 1804 | ||
| 1805 | /** | |
| 1806 | * List of tags allowed by content_filter_xss(). | |
| 1807 | */ | |
| 1808 | function _content_filter_xss_allowed_tags() { | |
| 6ff2552c | 1809 | return array('a', 'b', 'big', 'code', 'del', 'em', 'i', 'ins', 'pre', 'q', 'small', 'span', 'strong', 'sub', 'sup', 'tt', 'ol', 'ul', 'li', 'p', 'br', 'img'); |
| eb0954b8 YC |
1810 | } |
| 1811 | ||
| 1812 | /** | |
| 1813 | * Human-readable list of allowed tags, for display in help texts. | |
| 1814 | */ | |
| 1815 | function _content_filter_xss_display_allowed_tags() { | |
| 1816 | return '<'. implode('> <', _content_filter_xss_allowed_tags()) .'>'; | |
| 1817 | } | |
| 1818 | ||
| 1819 | /** | |
| a5dc9b5d KS |
1820 | * Format a field item for display. |
| 1821 | * | |
| bc789494 KS |
1822 | * Used to display a field's values outside the context of the $node, as |
| 1823 | * when fields are displayed in Views, or to display a field in a template | |
| 1824 | * using a different formatter than the one set up on the Display Fields tab | |
| 1825 | * for the node's context. | |
| 1826 | * | |
| a5dc9b5d KS |
1827 | * @param $field |
| 1828 | * Either a field array or the name of the field. | |
| 1829 | * @param $item | |
| e74ada9a YC |
1830 | * The field item(s) to be formatted (such as $node->field_foo[0], |
| 1831 | * or $node->field_foo if the formatter handles multiple values itself) | |
| f56a59dd | 1832 | * @param $formatter_name |
| a5dc9b5d KS |
1833 | * The name of the formatter to use. |
| 1834 | * @param $node | |
| e74ada9a YC |
1835 | * Optionally, the containing node object for context purposes and |
| 1836 | * field-instance options. | |
| a5dc9b5d KS |
1837 | * |
| 1838 | * @return | |
| e74ada9a | 1839 | * A string containing the contents of the field item(s) sanitized for display. |
| a5dc9b5d KS |
1840 | * It will have been passed through the necessary check_plain() or check_markup() |
| 1841 | * functions as necessary. | |
| 1842 | */ | |
| f56a59dd | 1843 | function content_format($field, $item, $formatter_name = 'default', $node = NULL) { |
| a5dc9b5d KS |
1844 | if (!is_array($field)) { |
| 1845 | $field = content_fields($field); | |
| 1846 | } | |
| e74ada9a | 1847 | |
| c7d4ceea | 1848 | if (content_access('view', $field, NULL, $node) && $formatter = _content_get_formatter($formatter_name, $field['type'])) { |
| f56a59dd YC |
1849 | $theme = $formatter['module'] .'_formatter_'. $formatter_name; |
| 1850 | ||
| f56a59dd YC |
1851 | $element = array( |
| 1852 | '#theme' => $theme, | |
| 1853 | '#field_name' => $field['field_name'], | |
| 89c0777d | 1854 | '#type_name' => isset($node->type) ? $node->type :'', |
| f56a59dd YC |
1855 | '#formatter' => $formatter_name, |
| 1856 | '#node' => $node, | |
| 1857 | '#delta' => isset($item['#delta']) ? $item['#delta'] : NULL, | |
| 1858 | ); | |
| 0c57bd61 | 1859 | |
| f56a59dd YC |
1860 | if (content_handle('formatter', 'multiple values', $formatter) == CONTENT_HANDLE_CORE) { |
| 1861 | // Single value formatter. | |
| e74ada9a | 1862 | |
| f56a59dd YC |
1863 | // hook_field('sanitize') expects an array of items, so we build one. |
| 1864 | $items = array($item); | |
| 1865 | $function = $field['module'] .'_field'; | |
| 1866 | if (function_exists($function)) { | |
| 1867 | $function('sanitize', $node, $field, $items, FALSE, FALSE); | |
| 1868 | } | |
| e74ada9a | 1869 | |
| f56a59dd | 1870 | $element['#item'] = $items[0]; |
| e74ada9a | 1871 | } |
| f56a59dd YC |
1872 | else { |
| 1873 | // Multiple values formatter. | |
| 1874 | $items = $item; | |
| 1875 | $function = $field['module'] .'_field'; | |
| 1876 | if (function_exists($function)) { | |
| 1877 | $function('sanitize', $node, $field, $items, FALSE, FALSE); | |
| 1878 | } | |
| e74ada9a | 1879 | |
| f56a59dd YC |
1880 | foreach ($items as $delta => $item) { |
| 1881 | $element[$delta] = array( | |
| 1882 | '#item' => $item, | |
| 1883 | '#weight' => $delta, | |
| 1884 | ); | |
| 1885 | } | |
| e74ada9a | 1886 | } |
| 1b39d666 | 1887 | |
| f56a59dd YC |
1888 | return theme($theme, $element); |
| 1889 | } | |
| a5dc9b5d KS |
1890 | } |
| 1891 | ||
| 1892 | /** | |
| a0f12ddb YC |
1893 | * Registry of available node build modes. |
| 1894 | * | |
| 1895 | * @param $selector | |
| 1896 | * Determines what information should be returned. | |
| 1897 | * @return | |
| 1898 | * Depending on the value of the $selector parameter: | |
| 1899 | * - NULL: a flat list of all available build modes. | |
| 1900 | * The other two options are mainly used internally by CCK's UI: | |
| 1901 | * - '_tabs': the list of tabs to be shown on the 'Display fields' screens. | |
| 1902 | * - a string tab id: the build modes in this tab. | |
| 94633da5 | 1903 | */ |
| a0f12ddb YC |
1904 | function content_build_modes($selector = NULL) { |
| 1905 | static $info; | |
| 6582b6ef | 1906 | |
| a0f12ddb | 1907 | if (!isset($info)) { |
| 5b740c2e YC |
1908 | $data = array(); |
| 1909 | foreach (module_implements('content_build_modes') as $module) { | |
| 1910 | $function = $module .'_content_build_modes'; | |
| 1911 | $data = array_merge($data, (array) $function()); | |
| 1912 | } | |
| a0f12ddb YC |
1913 | $flat = array(); |
| 1914 | foreach ($data as $tab) { | |
| ce88de23 YC |
1915 | // Use the + operator to preserve numeric indexes (core build modes). |
| 1916 | $flat += (array) $tab['build modes']; | |
| a0f12ddb YC |
1917 | } |
| 1918 | $info = array('tabs' => $data, 'build modes' => $flat); | |
| 6582b6ef YC |
1919 | } |
| 1920 | ||
| a0f12ddb YC |
1921 | if ($selector === '_tabs') { |
| 1922 | return $info['tabs']; | |
| 94633da5 | 1923 | } |
| a0f12ddb YC |
1924 | elseif (isset($selector) && isset($info['tabs'][$selector])) { |
| 1925 | return isset($info['tabs'][$selector]) ? $info['tabs'][$selector]['build modes'] : array(); | |
| 1926 | } | |
| 1927 | else { | |
| 1928 | return $info['build modes']; | |
| 1929 | } | |
| 1930 | } | |
| 94633da5 | 1931 | |
| a0f12ddb YC |
1932 | /** |
| 1933 | * Implementations of hook_content_build_modes | |
| 1934 | * on behalf of core modules. | |
| 1935 | * | |
| 1936 | * @return | |
| 1937 | * An array describing the build modes used by the module. | |
| 1938 | * They are grouped by secondary tabs on CCK's 'Display fields' screens. | |
| 1939 | * | |
| 1940 | * Expected format: | |
| 1941 | * array( | |
| 1942 | * // The first level keys (tab1_url, tab2_url) will be used to generate | |
| 1943 | * // the url of the tab: admin/content/node-type/[type_name]/display/[tab1_url] | |
| 1944 | * // A module can add its render modes to a tab defined by another module. | |
| 1945 | * // In this case, there's no need to provide a 'title' for this tab. | |
| 1946 | * 'tab1_url' => array( | |
| 1947 | * 'title' => t('The human-readable title of the tab'), | |
| 1948 | * 'build modes' => array( | |
| 1949 | * // The keys of the 'context' array are the values used in $node->build_mode. | |
| 1950 | * 'mymodule_mode1' => array( | |
| 1951 | * 'title' => t('The human-readable name of the build mode'), | |
| 1952 | * // The 'views style' property determines if the render mode should be | |
| 1953 | * // available as an option in Views' 'node' row style (not implemented yet). | |
| 1954 | * 'views style' => TRUE, | |
| 1955 | * ), | |
| 1956 | * 'mymodule_mode2' => array( | |
| 1957 | * 'title' => t('Mode 2'), | |
| 1958 | * 'views style' => TRUE, | |
| 1959 | * ), | |
| 1960 | * ), | |
| 1961 | * ), | |
| 1962 | * 'tab2_url' => array( | |
| 1963 | * // ... | |
| 1964 | * ), | |
| 1965 | * ); | |
| 1966 | */ | |
| 1967 | function node_content_build_modes() { | |
| 1968 | return array( | |
| 1969 | 'basic' => array( | |
| 1970 | 'title' => t('Basic'), | |
| 1971 | 'build modes' => array( | |
| 1972 | 'teaser' => array( | |
| 1973 | 'title' => t('Teaser'), | |
| 1974 | 'views style' => TRUE, | |
| 1975 | ), | |
| 1976 | 'full' => array( | |
| 1977 | 'title' => t('Full node'), | |
| 1978 | 'views style' => TRUE, | |
| 1979 | ), | |
| 1980 | ), | |
| 1981 | ), | |
| 1982 | 'rss' => array( | |
| 1983 | 'title' => t('RSS'), | |
| 1984 | 'build modes' => array( | |
| 1985 | NODE_BUILD_RSS => array( | |
| 1986 | 'title' => t('RSS'), | |
| 1987 | 'views style' => FALSE, | |
| 1988 | ), | |
| 1989 | ), | |
| 1990 | ), | |
| 1991 | ); | |
| 1992 | } | |
| 1993 | function search_content_build_modes() { | |
| 1994 | return array( | |
| 1995 | 'search' => array( | |
| 1996 | 'title' => t('Search'), | |
| 1997 | 'build modes' => array( | |
| 1998 | NODE_BUILD_SEARCH_INDEX => array( | |
| 1999 | 'title' => t('Search Index'), | |
| 2000 | 'views style' => FALSE, | |
| 2001 | ), | |
| 2002 | NODE_BUILD_SEARCH_RESULT => array( | |
| 2003 | 'title' => t('Search Result'), | |
| 2004 | 'views style' => FALSE, | |
| 2005 | ), | |
| 2006 | ), | |
| 2007 | ), | |
| 2008 | ); | |
| 94633da5 | 2009 | } |
| f4461172 YC |
2010 | function book_content_build_modes() { |
| 2011 | return array( | |
| 2012 | 'print' => array( | |
| 2013 | 'title' => t('Print'), | |
| 2014 | 'build modes' => array( | |
| 2015 | NODE_BUILD_PRINT => array( | |
| 2016 | 'title' => t('Print'), | |
| 2017 | 'views style' => TRUE, | |
| 2018 | ), | |
| 2019 | ), | |
| 2020 | ), | |
| 2021 | ); | |
| 2022 | } | |
| 94633da5 KS |
2023 | |
| 2024 | /** | |
| f44d1a5d YC |
2025 | * Generate a table name for a field or a content type. |
| 2026 | * | |
| 2027 | * @param $name | |
| 2028 | * The name of the content type or content field | |
| 734116ed YC |
2029 | * @param $storage |
| 2030 | * CONTENT_DB_STORAGE_PER_FIELD or CONTENT_DB_STORAGE_PER_CONTENT_TYPE | |
| f44d1a5d YC |
2031 | * @return |
| 2032 | * A string containing the generated name for the database table | |
| 2033 | */ | |
| 734116ed | 2034 | function _content_tablename($name, $storage, $version = NULL) { |
| 6fa59346 YC |
2035 | if (is_null($version)) { |
| 2036 | $version = variable_get('content_schema_version', 0); | |
| 2037 | } | |
| 2038 | ||
| 2039 | if ($version < 1003) { | |
| 2040 | $version = 0; | |
| 2041 | } | |
| 2042 | else { | |
| 2043 | $version = 1003; | |
| 2044 | } | |
| 2045 | ||
| 2046 | $name = str_replace('-', '_', $name); | |
| 734116ed | 2047 | switch ("$version-$storage") { |
| 6fa59346 | 2048 | case '0-'. CONTENT_DB_STORAGE_PER_CONTENT_TYPE : |
| 147b0a55 | 2049 | return "node_$name"; |
| 6fa59346 | 2050 | case '0-'. CONTENT_DB_STORAGE_PER_FIELD : |
| 147b0a55 | 2051 | return "node_data_$name"; |
| 6fa59346 | 2052 | case '1003-'. CONTENT_DB_STORAGE_PER_CONTENT_TYPE : |
| 147b0a55 | 2053 | return "content_type_$name"; |
| 6fa59346 | 2054 | case '1003-'. CONTENT_DB_STORAGE_PER_FIELD : |
| 147b0a55 | 2055 | return "content_$name"; |
| f44d1a5d YC |
2056 | } |
| 2057 | } | |
| 6fa59346 | 2058 | |
| b4ad53bb KS |
2059 | /** |
| 2060 | * Generate table name for the content field table. | |
| 2061 | * | |
| 2062 | * Needed because the table name changes depending on version. | |
| 0a3a3ce2 KS |
2063 | * Using 'content_node_field' instead of 'content_field' |
| 2064 | * to avoid conflicts with field tables that will be prefixed | |
| 2065 | * with 'content_field'. | |
| b4ad53bb | 2066 | */ |
| 6e0b21c2 | 2067 | function content_field_tablename($version = NULL) { |
| f4db2820 KS |
2068 | if (is_null($version)) { |
| 2069 | $version = variable_get('content_schema_version', 0); | |
| 2070 | } | |
| 0a3a3ce2 | 2071 | return $version < 6001 ? 'node_field' : 'content_node_field'; |
| f4db2820 KS |
2072 | } |
| 2073 | ||
| b4ad53bb KS |
2074 | /** |
| 2075 | * Generate table name for the content field instance table. | |
| 2076 | * | |
| 2077 | * Needed because the table name changes depending on version. | |
| 2078 | */ | |
| 6e0b21c2 | 2079 | function content_instance_tablename($version = NULL) { |
| f4db2820 KS |
2080 | if (is_null($version)) { |
| 2081 | $version = variable_get('content_schema_version', 0); | |
| 2082 | } | |
| 0a3a3ce2 | 2083 | return $version < 6001 ? 'node_field_instance' : 'content_node_field_instance'; |
| f4db2820 KS |
2084 | } |
| 2085 | ||
| b4ad53bb | 2086 | /** |
| 6e0b21c2 KS |
2087 | * Generate table name for the content cache table. |
| 2088 | * | |
| d1539c09 KS |
2089 | * Needed because the table name changes depending on version. Because of |
| 2090 | * a new database column, the content_cache table will be unusable until | |
| 2091 | * update 6000 runs, so the cache table will be used instead. | |
| d584405d YC |
2092 | */ |
| 2093 | function content_cache_tablename() { | |
| d1539c09 | 2094 | if (variable_get('content_schema_version', -1) < 6000) { |
| 6e0b21c2 KS |
2095 | return 'cache'; |
| 2096 | } | |
| 2097 | else { | |
| 2098 | return 'cache_content'; | |
| 2099 | } | |
| 2100 | } | |
| 2101 | ||
| 2102 | /** | |
| b4ad53bb KS |
2103 | * A basic schema used by all field and type tables. |
| 2104 | * | |
| d584405d | 2105 | * This will only add the columns relevant for the specified field. |
| 0a3a3ce2 KS |
2106 | * Leave $field['columns'] empty to get only the base schema, |
| 2107 | * otherwise the function will return the whole thing. | |
| b4ad53bb | 2108 | */ |
| 77ee7e3b | 2109 | function content_table_schema($field = NULL) { |
| 0a3a3ce2 | 2110 | $schema = array( |
| 734116ed YC |
2111 | 'fields' => array( |
| 2112 | 'vid' => array('type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0), | |
| 2113 | 'nid' => array('type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0) | |
| 2114 | ), | |
| 2115 | 'primary key' => array('vid'), | |
| c9cc355c YC |
2116 | 'indexes' => array( |
| 2117 | 'nid' => array('nid'), | |
| 2118 | ), | |
| 734116ed | 2119 | ); |
| d584405d YC |
2120 | |
| 2121 | // Add delta column if needed. | |
| 0a3a3ce2 KS |
2122 | if (!empty($field['multiple'])) { |
| 2123 | $schema['fields']['delta'] = array('type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0); | |
| 2124 | $schema['primary key'][] = 'delta'; | |
| 2125 | } | |
| 2126 | $schema['content fields'] = array(); | |
| 2127 | ||
| d584405d | 2128 | // Add field columns column if needed. |
| 0a3a3ce2 KS |
2129 | // This function is called from install files where it is not safe |
| 2130 | // to use content_fields() or content_database_info(), so we | |
| 2131 | // just used the column values stored in the $field. | |
| 239b1508 KS |
2132 | // We also need the schema to include fields from disabled modules |
| 2133 | // or there will be no way to delete those fields. | |
| d584405d | 2134 | |
| 77ee7e3b | 2135 | if (!empty($field['columns'])) { |
| 0a3a3ce2 | 2136 | foreach ($field['columns'] as $column => $attributes) { |
| 77ee7e3b | 2137 | $column_name = $field['field_name'] .'_'. $column; |
| f79a09ae MFM |
2138 | if (isset($attributes['index']) && $attributes['index']) { |
| 2139 | $schema['indexes'][$column_name] = array($column_name); | |
| 2140 | unset($attributes['index']); | |
| 2141 | } | |
| 0a3a3ce2 KS |
2142 | unset($attributes['column']); |
| 2143 | unset($attributes['sortable']); | |
| 77ee7e3b | 2144 | $schema['fields'][$column_name] = $attributes; |
| 0a3a3ce2 | 2145 | } |
| d584405d | 2146 | $schema['content fields'][] = $field['field_name']; |
| 734116ed | 2147 | } |
| 0a3a3ce2 | 2148 | return $schema; |
| 734116ed YC |
2149 | } |
| 2150 | ||
| 8544624f | 2151 | /** |
| f79a09ae MFM |
2152 | * Checks if an index exists. |
| 2153 | * | |
| 2154 | * @todo: May we remove this funcion when implemented by Drupal core itself? | |
| 2155 | * @link http://drupal.org/node/360854 | |
| 839017b4 | 2156 | * @link http://dev.mysql.com/doc/refman/5.0/en/extended-show.html |
| f79a09ae MFM |
2157 | * |
| 2158 | * @param $table | |
| 2159 | * Name of the table. | |
| 2160 | * @param $name | |
| 2161 | * Name of the index. | |
| 2162 | * @return | |
| 2163 | * TRUE if the table exists. Otherwise FALSE. | |
| 2164 | */ | |
| 2165 | function content_db_index_exists($table, $name) { | |
| 2166 | global $db_type; | |
| 2167 | if ($db_type == 'mysql' || $db_type == 'mysqli') { | |
| 839017b4 MFM |
2168 | if (version_compare(db_version(), '5.0.3') < 0) { |
| 2169 | // Earlier versions of MySQL don't support a WHERE clause for SHOW. | |
| 2170 | $result = db_query('SHOW INDEX FROM {'. $table .'}'); | |
| 2171 | while ($row = db_fetch_array($result)) { | |
| 2172 | if ($row['Key_name'] == $name) { | |
| 2173 | return TRUE; | |
| 2174 | } | |
| 2175 | } | |
| 2176 | return FALSE; | |
| 2177 | } | |
| f79a09ae MFM |
2178 | return (bool)db_result(db_query("SHOW INDEX FROM {". $table ."} WHERE key_name = '$name'")); |
| 2179 | } | |
| 2180 | elseif ($db_type == 'pgsql') { | |
| 5172fd8f MFM |
2181 | // Note that the index names in Schema API for PostgreSQL are prefixed by |
| 2182 | // the table name and suffixed by '_idx'. | |
| 2183 | return (bool)db_result(db_query("SELECT COUNT(indexname) FROM pg_indexes WHERE indexname = '{". $table ."}_{$name}_idx'")); | |
| f79a09ae MFM |
2184 | } |
| 2185 | return FALSE; | |
| 2186 | } | |
| 2187 | ||
| 2188 | /** | |
| 48c9a6da | 2189 | * Helper function for determining the behavior of a field or a widget |
| e9b6b501 KS |
2190 | * with respect to a given operation. (currently used for field 'view', |
| 2191 | * and widget 'default values' and 'multiple values') | |
| 429d94dc YC |
2192 | * |
| 2193 | * @param $entity | |
| 2194 | * 'field' or 'widget' | |
| 429d94dc | 2195 | * @param $op |
| e9b6b501 | 2196 | * the name of the operation ('view', 'default value'...) |
| 8544624f KS |
2197 | * @param $field |
| 2198 | * The field array, including widget info. | |
| c4dae5d3 | 2199 | * @return |
| 429d94dc YC |
2200 | * CONTENT_CALLBACK_NONE - do nothing for this operation |
| 2201 | * CONTENT_CALLBACK_CUSTOM - use the module's callback function. | |
| 48c9a6da | 2202 | * CONTENT_CALLBACK_DEFAULT - use content module default behavior |
| 8544624f | 2203 | * |
| 8544624f | 2204 | */ |
| a4418efc | 2205 | function content_callback($entity, $op, $field) { |
| e9b6b501 KS |
2206 | switch ($entity) { |
| 2207 | case 'field': | |
| 0cb6d582 | 2208 | $info = module_invoke($field['module'], "field_info"); |
| 1e31f877 | 2209 | return isset($info[$field['type']]['callbacks'][$op]) ? $info[$field['type']]['callbacks'][$op] : CONTENT_CALLBACK_DEFAULT; |
| e9b6b501 KS |
2210 | |
| 2211 | case 'widget': | |
| 0cb6d582 | 2212 | $info = module_invoke($field['widget']['module'], "widget_info"); |
| 1e31f877 | 2213 | return isset($info[$field['widget']['type']]['callbacks'][$op]) ? $info[$field['widget']['type']]['callbacks'][$op] : CONTENT_CALLBACK_DEFAULT; |
| 8544624f KS |
2214 | } |
| 2215 | } | |
| 2216 | ||
| 2217 | /** | |
| e74ada9a YC |
2218 | * Helper function for determining the handling of a field, widget or |
| 2219 | * formatter with respect to a given operation. | |
| 2220 | * | |
| 2221 | * Currently used for widgets and formatters 'multiple values'. | |
| a4418efc KS |
2222 | * |
| 2223 | * @param $entity | |
| e74ada9a | 2224 | * 'field', 'widget' or 'formatter' |
| a4418efc KS |
2225 | * @param $op |
| 2226 | * the name of the operation ('default values'...) | |
| e74ada9a | 2227 | * @param $object |
| 3a2d35f5 | 2228 | * - if $entity is 'field' or 'widget': the field array, |
| e74ada9a | 2229 | * including widget info. |
| 3a2d35f5 | 2230 | * - if $entity is 'formater': the formatter array. |
| c4dae5d3 | 2231 | * @return |
| a4418efc | 2232 | * CONTENT_HANDLE_CORE - the content module handles this operation. |
| e74ada9a | 2233 | * CONTENT_HANDLE_MODULE - the implementing module handles this operation. |
| a4418efc | 2234 | */ |
| 4923c448 | 2235 | function content_handle($entity, $op, $object) { |
| a4418efc KS |
2236 | switch ($entity) { |
| 2237 | case 'field': | |
| 4923c448 | 2238 | $info = module_invoke($object['module'], "field_info"); |
| 1e31f877 | 2239 | return isset($info[$object['type']][$op]) ? $info[$object['type']][$op] : CONTENT_HANDLE_CORE; |
| a4418efc KS |
2240 | |
| 2241 | case 'widget': | |
| 4923c448 | 2242 | $info = module_invoke($object['widget']['module'], "widget_info"); |
| 1e31f877 | 2243 | return isset($info[$object['widget']['type']][$op]) ? $info[$object['widget']['type']][$op] : CONTENT_HANDLE_CORE; |
| e74ada9a YC |
2244 | |
| 2245 | case 'formatter': | |
| 2246 | // Much simpler, formatters arrays *are* the 'formatter_info' itself. | |
| 2247 | // We let content_handle deal with them only for code consistency. | |
| 4923c448 | 2248 | return isset($object[$op]) ? $object[$op] : CONTENT_HANDLE_CORE; |
| a4418efc KS |
2249 | } |
| 2250 | } | |
| 2251 | ||
| 2252 | /** | |
| 8544624f KS |
2253 | * Helper function to return the correct default value for a field. |
| 2254 | * | |
| 429d94dc YC |
2255 | * @param $node |
| 2256 | * The node. | |
| 8544624f KS |
2257 | * @param $field |
| 2258 | * The field array. | |
| 429d94dc YC |
2259 | * @param $items |
| 2260 | * The value of the field in the node. | |
| 8544624f KS |
2261 | * @return |
| 2262 | * The default value for that field. | |
| 2263 | */ | |
| cfff5104 | 2264 | function content_default_value(&$form, &$form_state, $field, $delta) { |
| 429d94dc YC |
2265 | $widget_types = _content_widget_types(); |
| 2266 | $module = $widget_types[$field['widget']['type']]['module']; | |
| 8544624f KS |
2267 | |
| 2268 | $default_value = array(); | |
| e9b6b501 | 2269 | if (!empty($field['widget']['default_value_php'])) { |
| 8544624f KS |
2270 | ob_start(); |
| 2271 | $result = eval($field['widget']['default_value_php']); | |
| 429d94dc | 2272 | ob_end_clean(); |
| 8544624f KS |
2273 | if (is_array($result)) { |
| 2274 | $default_value = $result; | |
| 2275 | } | |
| 8544624f KS |
2276 | } |
| 2277 | elseif (!empty($field['widget']['default_value'])) { | |
| 2278 | $default_value = $field['widget']['default_value']; | |
| 2279 | } | |
| 429d94dc | 2280 | return (array) $default_value; |
| e9b6b501 KS |
2281 | } |
| 2282 | ||
| bb068a3a YC |
2283 | /** |
| 2284 | * Determine whether the user has access to a given field. | |
| 2285 | * | |
| 2286 | * @param $op | |
| 2287 | * The operation to be performed. Possible values: | |
| 2288 | * - "edit" | |
| 2289 | * - "view" | |
| 2290 | * @param $field | |
| 2291 | * The field on which the operation is to be performed. | |
| 2292 | * @param $account | |
| 2293 | * (optional) The account to check, if not given use currently logged in user. | |
| c7d4ceea MFM |
2294 | * @param $node |
| 2295 | * (optional) The node on which the operation is to be performed. | |
| bb068a3a YC |
2296 | * @return |
| 2297 | * TRUE if the operation is allowed; | |
| 2298 | * FALSE if the operation is denied. | |
| 2299 | */ | |
| c7d4ceea | 2300 | function content_access($op, $field, $account = NULL, $node = NULL) { |
| bb068a3a YC |
2301 | global $user; |
| 2302 | ||
| 2303 | if (is_null($account)) { | |
| 2304 | $account = $user; | |
| 2305 | } | |
| 2306 | ||
| c7d4ceea | 2307 | $access = module_invoke_all('field_access', $op, $field, $account, $node); |
| cc0a82f3 | 2308 | foreach ($access as $value) { |
| bb068a3a YC |
2309 | if ($value === FALSE) { |
| 2310 | return FALSE; | |
| 2311 | } | |
| 2312 | } | |
| 2313 | return TRUE; | |
| 2314 | } | |
| 2315 | ||
| 37916f9a KS |
2316 | /** |
| 2317 | * Hide specified fields from the $content variable in node templates. | |
| 2318 | */ | |
| 2319 | function content_field_wrapper_post_render($content, $element) { | |
| 8dbf0d14 | 2320 | $field = content_fields($element['#field_name'], $element['#type_name']); |
| 9363897f KS |
2321 | if (theme('content_exclude', $content, $field, $element['#context'])) { |
| 2322 | return ''; | |
| 2323 | } | |
| 2324 | return $content; | |
| 37916f9a KS |
2325 | } |
| 2326 | ||
| 2327 | ||
| 2328 | /** | |
| 2329 | * 'Theme' function for a field's addition to $content. | |
| 2330 | * | |
| 2331 | * Adapts the all-inclusive $content variable in node templates to allow | |
| 77531020 | 2332 | * some field content to be excluded. This is a theme function, so it can be |
| 37916f9a | 2333 | * overridden in different themes to produce different results. |
| 77531020 YC |
2334 | * |
| 2335 | * The html for individual fields and groups are available in the | |
| 37916f9a | 2336 | * $FIELD_NAME_rendered and $GROUP_NAME_rendered variables. |
| 77531020 | 2337 | * |
| 37916f9a KS |
2338 | * This allows more flexibility in node templates : you can use custom markup |
| 2339 | * around a few specific fields, and print the rest of the node with $content. | |
| 2340 | * | |
| 2341 | * @param $content | |
| 2342 | * The themed content for this field or group. | |
| 77531020 | 2343 | * |
| 37916f9a KS |
2344 | * @param $object |
| 2345 | * The field or group array for this item. | |
| 2346 | * $object['#type_name'] holds the content type. | |
| 2347 | * $object['#field_name'] holds the field name (if a field). | |
| 2348 | * $object['#group_name'] holds the group name (if a group). | |
| 77531020 YC |
2349 | * $object['display_settings'] holds the display settings |
| 2350 | * for all contexts, in an array like: | |
| 37916f9a KS |
2351 | * $object['display_settings'] => array( |
| 2352 | * 'full' => array( | |
| 77531020 | 2353 | * 'format' => 'default', |
| 37916f9a KS |
2354 | * 'exclude' => 0, |
| 2355 | * ), | |
| 2356 | * 'teaser' => array( | |
| 77531020 | 2357 | * 'format' => 'default', |
| 37916f9a KS |
2358 | * 'exclude' => 1, |
| 2359 | * ), | |
| 2360 | * ); | |
| 77531020 | 2361 | * |
| 37916f9a KS |
2362 | * @param $context |
| 2363 | * The context for which the node is being rendered. | |
| 2364 | * Can be one of the following values : | |
| 2365 | * - 'teaser' | |
| 2366 | * - 'full' | |
| 2367 | * - NODE_BUILD_SEARCH_INDEX | |
| 2368 | * - NODE_BUILD_SEARCH_RESULT | |
| 2369 | * - NODE_BUILD_RSS | |
| 2370 | * - NODE_BUILD_PRINT | |
| e0a62116 YC |
2371 | * - ... any other custom build mode exposed by 3rd party modules using |
| 2372 | * hook_content_build_modes(). | |
| 37916f9a KS |
2373 | * |
| 2374 | * @return | |
| 9363897f | 2375 | * Whether or not content is to be added to $content in this context. |
| 37916f9a KS |
2376 | * Uses the value of the 'Exclude' checkbox for this field |
| 2377 | * as set on the Manage fields screen. | |
| 2378 | */ | |
| 9363897f | 2379 | function theme_content_exclude($content, $object, $context) { |
| 8dbf0d14 | 2380 | // The field may be missing info for $contexts added by modules |
| 907eb38e | 2381 | // enabled after the field was last edited. |
| 8dbf0d14 | 2382 | if (empty($object['display_settings']) |
| 907eb38e | 2383 | || empty($object['display_settings'][$context]) |
| 77531020 | 2384 | || !is_array($object['display_settings'][$context]) |
| 37916f9a | 2385 | || empty($object['display_settings'][$context]['exclude'])) { |
| 9363897f | 2386 | return FALSE; |
| 37916f9a KS |
2387 | } |
| 2388 | else { | |
| 9363897f | 2389 | return TRUE; |
| 37916f9a KS |
2390 | } |
| 2391 | } | |
| 2392 | ||
| e9b6b501 | 2393 | /** |
| ef664e13 | 2394 | * Theme preprocess function for field.tpl.php. |
| f0f562b3 KS |
2395 | * |
| 2396 | * The $variables array contains the following arguments: | |
| 2397 | * - $node | |
| 2398 | * - $field | |
| 2399 | * - $items | |
| 2400 | * - $teaser | |
| 2401 | * - $page | |
| 2402 | * | |
| 2403 | * @see field.tpl.php | |
| ef664e13 YC |
2404 | * |
| 2405 | * TODO : this should live in theme/theme.inc, but then the preprocessor | |
| 2406 | * doesn't get called when the theme overrides the template. Bug in theme layer ? | |
| f0f562b3 | 2407 | */ |
| 1fdfa6d9 | 2408 | function content_preprocess_content_field(&$variables) { |
| e74ada9a | 2409 | $element = $variables['element']; |
| e833a6ee | 2410 | $field = content_fields($element['#field_name'], $element['#node']->type); |
| e74ada9a YC |
2411 | |
| 2412 | $variables['node'] = $element['#node']; | |
| e833a6ee | 2413 | $variables['field'] = $field; |
| e74ada9a YC |
2414 | $variables['items'] = array(); |
| 2415 | ||
| e833a6ee | 2416 | if ($element['#single']) { |
| e74ada9a | 2417 | // Single value formatter. |
| e833a6ee YC |
2418 | foreach (element_children($element['items']) as $delta) { |
| 2419 | $variables['items'][$delta] = $element['items'][$delta]['#item']; | |
| 6154f3d7 | 2420 | // Use isset() to avoid undefined index message on #children when field values are empty. |
| c7e6e6fe | 2421 | $variables['items'][$delta]['view'] = isset($element['items'][$delta]['#children']) ? $element['items'][$delta]['#children'] : ''; |
| e74ada9a YC |
2422 | } |
| 2423 | } | |
| 2424 | else { | |
| 2425 | // Multiple values formatter. | |
| 2426 | // We display the 'all items' output as $items[0], as if it was the | |
| 2427 | // output of a single valued field. | |
| 2428 | // Raw values are still exposed for all items. | |
| 2429 | foreach (element_children($element['items']) as $delta) { | |
| e833a6ee | 2430 | $variables['items'][$delta] = $element['items'][$delta]['#item']; |
| e74ada9a YC |
2431 | } |
| 2432 | $variables['items'][0]['view'] = $element['items']['#children']; | |
| 2433 | } | |
| 2434 | ||
| e833a6ee YC |
2435 | $variables['teaser'] = $element['#teaser']; |
| 2436 | $variables['page'] = $element['#page']; | |
| e74ada9a | 2437 | |
| d6c5544d | 2438 | $field_empty = TRUE; |
| e74ada9a | 2439 | |
| d6c5544d | 2440 | foreach ($variables['items'] as $delta => $item) { |
| 36acb2b7 | 2441 | if (!isset($item['view']) || (empty($item['view']) && (string)$item['view'] !== '0')) { |
| d6c5544d KS |
2442 | $variables['items'][$delta]['empty'] = TRUE; |
| 2443 | } | |
| 2444 | else { | |
| 2445 | $field_empty = FALSE; | |
| 2446 | $variables['items'][$delta]['empty'] = FALSE; | |
| f0f562b3 KS |
2447 | } |
| 2448 | } | |
| e74ada9a | 2449 | |
| f0f562b3 KS |
2450 | $additions = array( |
| 2451 | 'field_type' => $field['type'], | |
| 2452 | 'field_name' => $field['field_name'], | |
| 2453 | 'field_type_css' => strtr($field['type'], '_', '-'), | |
| 2454 | 'field_name_css' => strtr($field['field_name'], '_', '-'), | |
| eb0954b8 | 2455 | 'label' => check_plain(t($field['widget']['label'])), |
| a8ea2012 | 2456 | 'label_display' => $element['#label_display'], |
| f0f562b3 | 2457 | 'field_empty' => $field_empty, |
| efa5f7e9 YC |
2458 | 'template_files' => array( |
| 2459 | 'content-field', | |
| 2460 | 'content-field-'. $element['#field_name'], | |
| 2461 | 'content-field-'. $element['#node']->type, | |
| 2462 | 'content-field-'. $element['#field_name'] .'-'. $element['#node']->type, | |
| 2463 | ), | |
| f0f562b3 KS |
2464 | ); |
| 2465 | $variables = array_merge($variables, $additions); | |
| 1fdfa6d9 | 2466 | } |
| e833a6ee | 2467 | |
| 1fdfa6d9 YC |
2468 | /** |
| 2469 | * Theme preprocess function for node. | |
| 2470 | * | |
| 2471 | * - Adds $FIELD_NAME_rendered variables | |
| 2472 | * containing the themed output for the whole field. | |
| 2473 | * - Adds the formatted values in the 'view' key of the items. | |
| 2474 | */ | |
| 2475 | function content_preprocess_node(&$vars) { | |
| 2476 | $additions = _content_field_invoke_default('preprocess_node', $vars['node']); | |
| 2477 | $vars = array_merge($vars, $additions); | |
| 8be1814e KS |
2478 | } |
| 2479 | ||
| 2480 | /** | |
| 239b1508 | 2481 | * Debugging using hook_content_fieldapi. |
| 8be1814e KS |
2482 | * |
| 2483 | * @TODO remove later | |
| 2484 | * | |
| c4dae5d3 YC |
2485 | * @param $op |
| 2486 | * @param $field | |
| 8be1814e KS |
2487 | */ |
| 2488 | function content_content_fieldapi($op, $field) { | |
| 2489 | if (module_exists('devel')) { | |
| 2490 | //dsm($op); | |
| 2491 | //dsm($field); | |
| 2492 | } | |
| edad4922 YC |
2493 | } |
| 2494 | ||
| 2495 | /** | |
| 2496 | * Implementation of hook_content_extra_fields. | |
| 2497 | * | |
| 2498 | * Informations for non-CCK 'node fields' defined in core. | |
| 2499 | */ | |
| 2500 | function content_content_extra_fields($type_name) { | |
| 2501 | $type = node_get_types('type', $type_name); | |
| 2502 | $extra = array(); | |
| 1b39d666 | 2503 | |
| edad4922 | 2504 | if ($type->has_title) { |
| feead187 YC |
2505 | $extra['title'] = array( |
| 2506 | 'label' => $type->title_label, | |
| 6afc8e97 | 2507 | 'description' => t('Node module form.'), |
| feead187 YC |
2508 | 'weight' => -5 |
| 2509 | ); | |
| edad4922 YC |
2510 | } |
| 2511 | if ($type->has_body) { | |
| feead187 YC |
2512 | $extra['body_field'] = array( |
| 2513 | 'label' => $type->body_label, | |
| 6afc8e97 | 2514 | 'description' => t('Node module form.'), |
| feead187 YC |
2515 | 'weight' => 0, |
| 2516 | 'view' => 'body' | |
| 2517 | ); | |
| edad4922 | 2518 | } |
| aad111ae KS |
2519 | $extra['revision_information'] = array( |
| 2520 | 'label' => t('Revision information'), | |
| 2521 | 'description' => t('Node module form.'), | |
| 2522 | 'weight' => 20 | |
| 2523 | ); | |
| b812fc29 | 2524 | if (module_exists('comment')) { |
| aad111ae KS |
2525 | $extra['comment_settings'] = array( |
| 2526 | 'label' => t('Comment settings'), | |
| 2527 | 'description' => t('Comment module form.'), | |
| 2528 | 'weight' => 30 | |
| 2529 | ); | |
| 2530 | } | |
| 99e73f7d | 2531 | if (module_exists('locale') && variable_get("language_content_type_$type_name", 0)) { |
| feead187 YC |
2532 | $extra['language'] = array( |
| 2533 | 'label' => t('Language'), | |
| 6afc8e97 | 2534 | 'description' => t('Locale module form.'), |
| feead187 YC |
2535 | 'weight' => 0 |
| 2536 | ); | |
| edad4922 | 2537 | } |
| b812fc29 MFM |
2538 | if (module_exists('translation') && translation_supported_type($type_name)) { |
| 2539 | $extra['translation'] = array( | |
| 2540 | 'label' => t('Translation settings'), | |
| 2541 | 'description' => t('Translation module form.'), | |
| 2542 | 'weight' => 30 | |
| 2543 | ); | |
| 2544 | } | |
| c3e14528 | 2545 | if (module_exists('menu')) { |
| feead187 | 2546 | $extra['menu'] = array( |
| 6afc8e97 KS |
2547 | 'label' => t('Menu settings'), |
| 2548 | 'description' => t('Menu module form.'), | |
| feead187 YC |
2549 | 'weight' => -2 |
| 2550 | ); | |
| c3e14528 | 2551 | } |
| edad4922 | 2552 | if (module_exists('taxonomy') && taxonomy_get_vocabularies($type_name)) { |
| feead187 YC |
2553 | $extra['taxonomy'] = array( |
| 2554 | 'label' => t('Taxonomy'), | |
| 6afc8e97 | 2555 | 'description' => t('Taxonomy module form.'), |
| feead187 YC |
2556 | 'weight' => -3 |
| 2557 | ); | |
| edad4922 | 2558 | } |
| 3ed2a150 KS |
2559 | if (module_exists('book')) { |
| 2560 | $extra['book'] = array( | |
| 2561 | 'label' => t('Book'), | |
| 2562 | 'description' => t('Book module form.'), | |
| 2563 | 'weight' => 10 | |
| 2564 | ); | |
| 2565 | } | |
| b812fc29 MFM |
2566 | if (module_exists('path')) { |
| 2567 | $extra['path'] = array( | |
| 2568 | 'label' => t('Path settings'), | |
| 2569 | 'description' => t('Path module form.'), | |
| 2570 | 'weight' => 30 | |
| 2571 | ); | |
| 2572 | } | |
| a5b811a2 KS |
2573 | if ($type_name == 'poll' && module_exists('poll')) { |
| 2574 | $extra['title'] = array( | |
| 2575 | 'label' => t('Poll title'), | |
| 2576 | 'description' => t('Poll module title.'), | |
| 2577 | 'weight' => -5 | |
| 2578 | ); | |
| 2579 | $extra['choice_wrapper'] = array( | |
| 2580 | 'label' => t('Poll choices'), | |
| 2581 | 'description' => t('Poll module choices.'), | |
| 2582 | 'weight' => -4 | |
| 2583 | ); | |
| 2584 | $extra['settings'] = array( | |
| 2585 | 'label' => t('Poll settings'), | |
| 2586 | 'description' => t('Poll module settings.'), | |
| 2587 | 'weight' => -3 | |
| 2588 | ); | |
| 1b39d666 | 2589 | } |
| 062f7350 | 2590 | if (module_exists('upload') && variable_get("upload_$type_name", TRUE)) { |
| feead187 YC |
2591 | $extra['attachments'] = array( |
| 2592 | 'label' => t('File attachments'), | |
| 6afc8e97 | 2593 | 'description' => t('Upload module form.'), |
| feead187 YC |
2594 | 'weight' => 30, |
| 2595 | 'view' => 'files' | |
| 2596 | ); | |
| edad4922 YC |
2597 | } |
| 2598 | ||
| 2599 | return $extra; | |
| 2600 | } | |
| 2601 | ||
| 2602 | /** | |
| 2603 | * Retrieve the user-defined weight for non-CCK node 'fields'. | |
| 2604 | * | |
| 2605 | * CCK's 'Manage fields' page lets users reorder node fields, including non-CCK | |
| 2606 | * items (body, taxonomy, other hook_nodeapi-added elements by contrib modules...). | |
| 2607 | * Contrib modules that want to have their 'fields' supported need to expose | |
| 2608 | * them with hook_content_extra_fields, and use this function to retrieve the | |
| 2609 | * user-defined weight. | |
| 2610 | * | |
| 2611 | * @param $type_name | |
| 2612 | * The content type name. | |
| 2613 | * @param $pseudo_field_name | |
| 2614 | * The name of the 'field'. | |
| 2615 | * @return | |
| 2616 | * The weight for the 'field', respecting the user settings stored | |
| 2617 | * by content.module. | |
| 2618 | */ | |
| 2619 | function content_extra_field_weight($type_name, $pseudo_field_name) { | |
| 617f405b | 2620 | $type = content_types($type_name); |
| edad4922 YC |
2621 | |
| 2622 | // If we don't have the requested item, this may be because the cached | |
| 2623 | // information for 'extra' fields hasn't been refreshed yet. | |
| 2624 | if (!isset($type['extra'][$pseudo_field_name])) { | |
| 2625 | content_clear_type_cache(); | |
| 7cbbf220 | 2626 | $type = content_types($type_name); |
| edad4922 | 2627 | } |
| 1b39d666 | 2628 | |
| edad4922 YC |
2629 | if (isset($type['extra'][$pseudo_field_name])) { |
| 2630 | return $type['extra'][$pseudo_field_name]['weight']; | |
| 2631 | } | |
| 2632 | } | |
| b7eb74cb KS |
2633 | |
| 2634 | /** | |
| 2635 | * Find max delta value actually in use for a field. | |
| d1eac231 KS |
2636 | * |
| 2637 | * Helper function to do things like tell when we should prevent a | |
| b7eb74cb KS |
2638 | * change in multiple value settings that would result in data loss, |
| 2639 | * or know if content actually exists for a field. | |
| d1eac231 KS |
2640 | * |
| 2641 | * @param $field_name | |
| b7eb74cb | 2642 | * The field name to examine. |
| d1eac231 | 2643 | * @param $type_name |
| b7eb74cb KS |
2644 | * If provided, search only for existing data in that type, |
| 2645 | * otherwise search for all instances of field data in all types. | |
| d1eac231 | 2646 | * @return |
| b7eb74cb | 2647 | * NULL if field is not in use, or the maximum delta value in use. |
| d1eac231 | 2648 | * |
| b7eb74cb KS |
2649 | * TODO |
| 2650 | * Go back to the field settings validation and use this function | |
| d1eac231 | 2651 | * to prevent (or confirm) changes in multiple values that |
| b7eb74cb | 2652 | * would destroy data. |
| d1eac231 | 2653 | * |
| b7eb74cb KS |
2654 | * Fields with only NULL data will show up as being in use. |
| 2655 | * Do we want to eliminate them from the results? | |
| 2656 | */ | |
| 2657 | function content_max_delta($field_name, $type_name = NULL) { | |
| 2658 | $fields = content_fields(); | |
| 2659 | $field = $fields[$field_name]; | |
| d1eac231 | 2660 | |
| b7eb74cb KS |
2661 | // Non-multiple value fields don't use the delta column, |
| 2662 | // but could exist in multiple databases. If any value | |
| 2663 | // exists in any examined table, the max delta will be zero. | |
| 2664 | if (empty($field['multiple'])) { | |
| 2665 | $content_types = content_types(); | |
| 2666 | foreach ($content_types as $content_type) { | |
| 2667 | if (empty($type_name) || $content_type['type'] == $type_name) { | |
| 2668 | foreach ($content_type['fields'] as $field) { | |
| 2669 | $db_info = content_database_info($field); | |
| 39308765 | 2670 | if (db_result(db_query("SELECT COUNT(*) FROM {". $db_info['table'] ."}")) >= 1) { |
| b7eb74cb KS |
2671 | return 0; |
| 2672 | } | |
| 2673 | } | |
| 2674 | } | |
| 2675 | } | |
| 2676 | } | |
| 2677 | // Multiple value fields always share the same table and use the delta. | |
| 2678 | // If we want to find delta values for a particular type, we join | |
| 2679 | // in the node table to limit the type. | |
| 2680 | else { | |
| 2681 | $db_info = content_database_info($field); | |
| b7eb74cb | 2682 | if (!empty($type_name)) { |
| 39308765 | 2683 | $delta = db_result(db_query("SELECT MAX(delta) FROM {". $db_info['table'] ."} f LEFT JOIN {node} n ON f.vid = n.vid WHERE n.type = '%s'", $type_name)); |
| b7eb74cb KS |
2684 | } |
| 2685 | else { | |
| 39308765 | 2686 | $delta = db_result(db_query("SELECT MAX(delta) FROM {". $db_info['table'] ."}")); |
| b7eb74cb KS |
2687 | } |
| 2688 | if ($delta >= 0) { | |
| 2689 | return $delta; | |
| 2690 | } | |
| 2691 | } | |
| 2692 | // If we got this far, there is no data for this field. | |
| 2693 | return NULL; | |
| 2694 | } | |
| 876a5b2c KS |
2695 | |
| 2696 | /** | |
| 2697 | * Helper function to identify inactive fields. | |
| 2698 | */ | |
| 2699 | function content_inactive_fields($type_name = NULL) { | |
| 2700 | module_load_include('inc', 'content', 'includes/content.crud'); | |
| 2701 | if (!empty($type_name)) { | |
| 2702 | $param = array('type_name' => $type_name); | |
| 2703 | $inactive = array($type_name => array()); | |
| 2704 | } | |
| 2705 | else { | |
| 2706 | $param = array(); | |
| 2707 | $inactive = array(); | |
| 2708 | } | |
| 2709 | $all = content_field_instance_read($param, TRUE); | |
| 2710 | $active = array_keys(content_fields()); | |
| 2711 | foreach ($all as $field) { | |
| 81f9af51 | 2712 | if (!in_array($field['field_name'], $active)) { |
| 876a5b2c KS |
2713 | $inactive[$field['type_name']][$field['field_name']] = content_field_instance_expand($field); |
| 2714 | } | |
| 2715 | } | |
| 2716 | if (!empty($type_name)) { | |
| 2717 | return $inactive[$type_name]; | |
| 2718 | } | |
| 2719 | return $inactive; | |
| 37916f9a | 2720 | } |