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 &lt;?php ?&gt; 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('&lt;none&gt;'),
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