1<?php 2// $Id$ 3 4/** 5 * @file 6 * Defines simple text field types. 7 */ 8 9/** 10 * Implementation of hook_theme(). 11 */ 12function text_theme() { 13 return array( 14 'text_textarea' => array( 15 'arguments' => array('element' => NULL), 16 ), 17 'text_textfield' => array( 18 'arguments' => array('element' => NULL), 19 ), 20 'text_formatter_default' => array( 21 'arguments' => array('element' => NULL), 22 ), 23 'text_formatter_plain' => array( 24 'arguments' => array('element' => NULL), 25 ), 26 'text_formatter_trimmed' => array( 27 'arguments' => array('element' => NULL), 28 ), 29 'text_formatter_foo' => array( 30 'arguments' => array('element' => NULL), 31 ), 32 ); 33} 34 35/** 36 * Implementation of hook_field_info(). 37 */ 38function text_field_info() { 39 return array( 40 'text' => array( 41 'label' => t('Text'), 42 'description' => t('Store text in the database.'), 43// 'content_icon' => 'icon_content_text.png', 44 ), 45 ); 46} 47 48/** 49 * Implementation of hook_field_settings(). 50 */ 51function text_field_settings($op, $field) { 52 switch ($op) { 53 case 'form': 54 $form = array(); 55 $options = array(0 => t('Plain text'), 1 => t('Filtered text (user selects input format)')); 56 $form['text_processing'] = array( 57 '#type' => 'radios', 58 '#title' => t('Text processing'), 59 '#default_value' => is_numeric($field['text_processing']) ? $field['text_processing'] : 0, 60 '#options' => $options, 61 ); 62 $form['max_length'] = array( 63 '#type' => 'textfield', 64 '#title' => t('Maximum length'), 65 '#default_value' => is_numeric($field['max_length']) ? $field['max_length'] : '', 66 '#required' => FALSE, 67 '#element_validate' => array('_element_validate_integer_positive'), 68 '#description' => t('The maximum length of the field in characters. Leave blank for an unlimited size.'), 69 ); 70 $form['allowed_values_fieldset'] = array( 71 '#type' => 'fieldset', 72 '#title' => t('Allowed values'), 73 '#collapsible' => TRUE, 74 '#collapsed' => TRUE, 75 ); 76 $form['allowed_values_fieldset']['allowed_values'] = array( 77 '#type' => 'textarea', 78 '#title' => t('Allowed values list'), 79 '#default_value' => !empty($field['allowed_values']) ? $field['allowed_values'] : '', 80 '#required' => FALSE, 81 '#rows' => 10, 82 '#description' => t('The possible values this field can contain. Enter one value per line, in the format key|label. The key is the value that will be stored in the database, and it must match the field storage type (%type). The label is optional, and the key will be used as the label if no label is specified.<br />Allowed HTML tags: @tags', array('%type' => $field['type'], '@tags' => _content_filter_xss_display_allowed_tags())), 83 ); 84 $form['allowed_values_fieldset']['advanced_options'] = array( 85 '#type' => 'fieldset', 86 '#title' => t('PHP code'), 87 '#collapsible' => TRUE, 88 '#collapsed' => empty($field['allowed_values_php']), 89 ); 90 if (user_access('Use PHP input for field settings (dangerous - grant with care)')) { 91 $form['allowed_values_fieldset']['advanced_options']['allowed_values_php'] = array( 92 '#type' => 'textarea', 93 '#title' => t('Code'), 94 '#default_value' => !empty($field['allowed_values_php']) ? $field['allowed_values_php'] : '', 95 '#rows' => 6, 96 '#description' => t('Advanced usage only: PHP code that returns a keyed array of allowed values. Should not include <?php ?> delimiters. If this field is filled out, the array returned by this code will override the allowed values list above.'), 97 ); 98 } 99 else { 100 $form['allowed_values_fieldset']['advanced_options']['markup_allowed_values_php'] = array( 101 '#type' => 'item', 102 '#title' => t('Code'), 103 '#value' => !empty($field['allowed_values_php']) ? '<code>'. check_plain($field['allowed_values_php']) .'</code>' : t('<none>'), 104 '#description' => empty($field['allowed_values_php']) ? t("You're not allowed to input PHP code.") : t('This PHP code was set by an administrator and will override the allowed values list above.'), 105 ); 106 } 107 return $form; 108 109 case 'save': 110 return array('text_processing', 'max_length', 'allowed_values', 'allowed_values_php'); 111 112 case 'database columns': 113 if (empty($field['max_length']) || $field['max_length'] > 255) { 114 $columns['value'] = array('type' => 'text', 'size' => 'big', 'not null' => FALSE, 'sortable' => TRUE, 'views' => TRUE); 115 } 116 else { 117 $columns['value'] = array('type' => 'varchar', 'length' => $field['max_length'], 'not null' => FALSE, 'sortable' => TRUE, 'views' => TRUE); 118 } 119 if (!empty($field['text_processing'])) { 120 $columns['format'] = array('type' => 'int', 'unsigned' => TRUE, 'not null' => FALSE, 'views' => FALSE); 121 } 122 return $columns; 123 124 case 'views data': 125 $allowed_values = content_allowed_values($field); 126 if (count($allowed_values)) { 127 $data = content_views_field_views_data($field); 128 $db_info = content_database_info($field); 129 $table_alias = content_views_tablename($field); 130 131 // Filter: Add a 'many to one' filter. 132 $copy = $data[$table_alias][$field['field_name'] .'_value']; 133 $copy['title'] = t('@label (!name) - Allowed values', array('@label' => t($field['widget']['label']), '!name' => $field['field_name'])); 134 $copy['filter']['handler'] = 'content_handler_filter_many_to_one'; 135 unset($copy['field'], $copy['argument'], $copy['sort']); 136 $data[$table_alias][$field['field_name'] .'_value_many_to_one'] = $copy; 137 // Argument : swap the handler to the 'many to one' operator. 138 $data[$table_alias][$field['field_name'] .'_value']['argument']['handler'] = 'content_handler_argument_many_to_one'; 139 return $data; 140 } 141 } 142} 143 144/** 145 * Implementation of hook_field(). 146 */ 147function text_field($op, &$node, $field, &$items, $teaser, $page) { 148 switch ($op) { 149 case 'validate': 150 $allowed_values = content_allowed_values($field); 151 if (is_array($items)) { 152 foreach ($items as $delta => $item) { 153 $error_element = isset($item['_error_element']) ? $item['_error_element'] : ''; 154 if (is_array($item) && isset($item['_error_element'])) unset($item['_error_element']); 155 if (!empty($item['value'])) { 156 if (count($allowed_values) && !array_key_exists($item['value'], $allowed_values)) { 157 form_set_error($error_element, t('%name: illegal value.', array('%name' => t($field['widget']['label'])))); 158 } 159 if (!empty($field['max_length']) && drupal_strlen($item['value']) > $field['max_length']) { 160 form_set_error($error_element, t('%name: the value may not be longer than %max characters.', array('%name' => $field['widget']['label'], '%max' => $field['max_length']))); 161 } 162 } 163 } 164 } 165 return $items; 166 167 case 'sanitize': 168 foreach ($items as $delta => $item) { 169 if (!empty($field['text_processing'])) { 170 $text = isset($item['value']) ? check_markup($item['value'], $item['format'], FALSE) : ''; 171 } 172 else { 173 $text = isset($item['value']) ? check_plain($item['value']) : ''; 174 } 175 $items[$delta]['safe'] = $text; 176 } 177 } 178} 179 180/** 181 * Implementation of hook_content_is_empty(). 182 */ 183function text_content_is_empty($item, $field) { 184 if (empty($item['value']) && (string)$item['value'] !== '0') { 185 return TRUE; 186 } 187 return FALSE; 188} 189 190/** 191 * Implementation of hook_field_formatter_info(). 192 */ 193function text_field_formatter_info() { 194 return array( 195 'default' => array( 196 'label' => t('Default'), 197 'field types' => array('text'), 198 'multiple values' => CONTENT_HANDLE_CORE, 199 ), 200 'plain' => array( 201 'label' => t('Plain text'), 202 'field types' => array('text'), 203 'multiple values' => CONTENT_HANDLE_CORE, 204 ), 205 'trimmed' => array( 206 'label' => t('Trimmed'), 207 'field types' => array('text'), 208 'multiple values' => CONTENT_HANDLE_CORE, 209 ), 210 ); 211} 212 213/** 214 * Theme function for 'default' text field formatter. 215 */ 216function theme_text_formatter_default($element) { 217 return ($allowed =_text_allowed_values($element)) ? $allowed : $element['#item']['safe']; 218} 219 220/** 221 * Theme function for 'plain' text field formatter. 222 */ 223function theme_text_formatter_plain($element) { 224 return ($allowed =_text_allowed_values($element)) ? $allowed : strip_tags($element['#item']['safe']); 225} 226 227/** 228 * Theme function for 'trimmed' text field formatter. 229 */ 230function theme_text_formatter_trimmed($element) { 231 $field = content_fields($element['#field_name'], $element['#type_name']); 232 return ($allowed =_text_allowed_values($element)) ? $allowed : node_teaser($element['#item']['safe'], $field['text_processing'] ? $element['#item']['format'] : NULL); 233} 234 235function _text_allowed_values($element) { 236 $field = content_fields($element['#field_name'], $element['#type_name']); 237 if (($allowed_values = content_allowed_values($field)) && isset($allowed_values[$element['#item']['value']])) { 238 return $allowed_values[$element['#item']['value']]; 239 } 240} 241 242/** 243 * Implementation of hook_widget_info(). 244 * 245 * Here we indicate that the content module will handle 246 * the default value and multiple values for these widgets. 247 * 248 * Callbacks can be omitted if default handing is used. 249 * They're included here just so this module can be used 250 * as an example for custom modules that might do things 251 * differently. 252 */ 253function text_widget_info() { 254 return array( 255 'text_textfield' => array( 256 'label' => t('Text field'), 257 'field types' => array('text'), 258 'multiple values' => CONTENT_HANDLE_CORE, 259 'callbacks' => array( 260 'default value' => CONTENT_CALLBACK_DEFAULT, 261 ), 262 ), 263 'text_textarea' => array( 264 'label' => t('Text area (multiple rows)'), 265 'field types' => array('text'), 266 'multiple values' => CONTENT_HANDLE_CORE, 267 'callbacks' => array( 268 'default value' => CONTENT_CALLBACK_DEFAULT, 269 ), 270 ), 271 ); 272} 273 274/** 275 * Implementation of FAPI hook_elements(). 276 * 277 * Any FAPI callbacks needed for individual widgets can be declared here, 278 * and the element will be passed to those callbacks for processing. 279 * 280 * Drupal will automatically theme the element using a theme with 281 * the same name as the hook_elements key. 282 * 283 * Autocomplete_path is not used by text_widget but other widgets can use it 284 * (see nodereference and userreference). 285 */ 286function text_elements() { 287 return array( 288 'text_textfield' => array( 289 '#input' => TRUE, 290 '#columns' => array('value'), '#delta' => 0, 291 '#process' => array('text_textfield_process'), 292 '#autocomplete_path' => FALSE, 293 ), 294 'text_textarea' => array( 295 '#input' => TRUE, 296 '#columns' => array('value', 'format'), '#delta' => 0, 297 '#process' => array('text_textarea_process'), 298 '#filter_value' => FILTER_FORMAT_DEFAULT, 299 ), 300 ); 301} 302 303/** 304 * Implementation of hook_widget_settings(). 305 */ 306function text_widget_settings($op, $widget) { 307 switch ($op) { 308 case 'form': 309 $form = array(); 310 $rows = (isset($widget['rows']) && is_numeric($widget['rows'])) ? $widget['rows'] : 5; 311 $size = (isset($widget['size']) && is_numeric($widget['size'])) ? $widget['size'] : 60; 312 if ($widget['type'] == 'text_textfield') { 313 $form['rows'] = array('#type' => 'hidden', '#value' => $rows); 314 $form['size'] = array( 315 '#type' => 'textfield', 316 '#title' => t('Size of textfield'), 317 '#default_value' => $size, 318 '#element_validate' => array('_element_validate_integer_positive'), 319 '#required' => TRUE, 320 ); 321 } 322 else { 323 $form['rows'] = array( 324 '#type' => 'textfield', 325 '#title' => t('Rows'), 326 '#default_value' => $rows, 327 '#element_validate' => array('_element_validate_integer_positive'), 328 '#required' => TRUE, 329 ); 330 $form['size'] = array('#type' => 'hidden', '#value' => $size); 331 } 332 return $form; 333 334 case 'save': 335 return array('rows', 'size'); 336 } 337} 338 339/** 340 * Implementation of hook_widget(). 341 * 342 * Attach a single form element to the form. It will be built out and 343 * validated in the callback(s) listed in hook_elements. We build it 344 * out in the callbacks rather than here in hook_widget so it can be 345 * plugged into any module that can provide it with valid 346 * $field information. 347 * 348 * Content module will set the weight, field name and delta values 349 * for each form element. This is a change from earlier CCK versions 350 * where the widget managed its own multiple values. 351 * 352 * If there are multiple values for this field, the content module will 353 * call this function as many times as needed. 354 * 355 * @param $form 356 * the entire form array, $form['#node'] holds node information 357 * @param $form_state 358 * the form_state, $form_state['values'][$field['field_name']] 359 * holds the field's form values. 360 * @param $field 361 * the field array 362 * @param $items 363 * array of default values for this field 364 * @param $delta 365 * the order of this item in the array of subelements (0, 1, 2, etc) 366 * 367 * @return 368 * the form item for a single element for this field 369 */ 370function text_widget(&$form, &$form_state, $field, $items, $delta = 0) { 371 $element = array( 372 '#type' => $field['widget']['type'], 373 '#default_value' => isset($items[$delta]) ? $items[$delta] : '', 374 ); 375 return $element; 376} 377 378/** 379 * Process an individual element. 380 * 381 * Build the form element. When creating a form using FAPI #process, 382 * note that $element['#value'] is already set. 383 * 384 * The $fields array is in $form['#field_info'][$element['#field_name']]. 385 */ 386function text_textfield_process($element, $edit, $form_state, $form) { 387 $field = $form['#field_info'][$element['#field_name']]; 388 $field_key = $element['#columns'][0]; 389 $delta = $element['#delta']; 390 $element[$field_key] = array( 391 '#type' => 'textfield', 392 '#default_value' => isset($element['#value'][$field_key]) ? $element['#value'][$field_key] : NULL, 393 '#autocomplete_path' => $element['#autocomplete_path'], 394 '#size' => !empty($field['widget']['size']) ? $field['widget']['size'] : 60, 395 '#attributes' => array('class' => 'text'), 396 // The following values were set by the content module and need 397 // to be passed down to the nested element. 398 '#title' => $element['#title'], 399 '#description' => $element['#description'], 400 '#required' => $element['#required'], 401 '#field_name' => $element['#field_name'], 402 '#type_name' => $element['#type_name'], 403 '#delta' => $element['#delta'], 404 '#columns' => $element['#columns'], 405 ); 406 407 $element[$field_key]['#maxlength'] = !empty($field['max_length']) ? $field['max_length'] : NULL; 408 409 if (!empty($field['text_processing'])) { 410 $filter_key = $element['#columns'][1]; 411 $format = isset($element['#value'][$filter_key]) ? $element['#value'][$filter_key] : FILTER_FORMAT_DEFAULT; 412 $parents = array_merge($element['#parents'] , array($filter_key)); 413 $element[$filter_key] = filter_form($format, 1, $parents); 414 } 415 416 // Used so that hook_field('validate') knows where to flag an error. 417 $element['_error_element'] = array( 418 '#type' => 'value', 419 '#value' => implode('][', array_merge($element['#parents'], array($field_key))), 420 ); 421 422 return $element; 423} 424 425/** 426 * Process an individual element. 427 * 428 * Build the form element. When creating a form using FAPI #process, 429 * note that $element['#value'] is already set. 430 * 431 * The $fields array is in $form['#field_info'][$element['#field_name']]. 432 */ 433function text_textarea_process($element, $edit, $form_state, $form) { 434 $field = $form['#field_info'][$element['#field_name']]; 435 $field_key = $element['#columns'][0]; 436 $element[$field_key] = array( 437 '#type' => 'textarea', 438 '#default_value' => isset($element['#value'][$field_key]) ? $element['#value'][$field_key] : NULL, 439 '#rows' => !empty($field['widget']['rows']) ? $field['widget']['rows'] : 10, 440 '#weight' => 0, 441 // The following values were set by the content module and need 442 // to be passed down to the nested element. 443 '#title' => $element['#title'], 444 '#description' => $element['#description'], 445 '#required' => $element['#required'], 446 '#field_name' => $element['#field_name'], 447 '#type_name' => $element['#type_name'], 448 '#delta' => $element['#delta'], 449 '#columns' => $element['#columns'], 450 ); 451 452 if (!empty($field['text_processing'])) { 453 $filter_key = (count($element['#columns']) == 2) ? $element['#columns'][1] : 'format'; 454 $format = isset($element['#value'][$filter_key]) ? $element['#value'][$filter_key] : FILTER_FORMAT_DEFAULT; 455 $parents = array_merge($element['#parents'] , array($filter_key)); 456 $element[$filter_key] = filter_form($format, 1, $parents); 457 } 458 459 // Used so that hook_field('validate') knows where to flag an error. 460 $element['_error_element'] = array( 461 '#type' => 'value', 462 '#value' => implode('][', array_merge($element['#parents'], array($field_key))), 463 ); 464 465 return $element; 466} 467 468/** 469 * FAPI theme for an individual text elements. 470 * 471 * The textfield or textarea is already rendered by the 472 * textfield or textarea themes and the html output 473 * lives in $element['#children']. Override this theme to 474 * make custom changes to the output. 475 * 476 * $element['#field_name'] contains the field name 477 * $element['#delta] is the position of this element in the group 478 */ 479function theme_text_textfield($element) { 480 return $element['#children']; 481} 482 483function theme_text_textarea($element) { 484 return $element['#children']; 485} 486