source: sipes/modules_contrib/content_taxonomy/content_taxonomy_autocomplete.module @ a8b1f3f

stableversion-3.0
Last change on this file since a8b1f3f was 177a560, checked in by José Gregorio Puentes <jpuentes@…>, 8 años ago

se agrego el directorio de modulos contribuidos de drupal

  • Propiedad mode establecida a 100755
File size: 15.6 KB
Línea 
1<?php
2// $Id: content_taxonomy_autocomplete.module,v 1.2.2.4.2.17 2009/08/19 09:42:30 mh86 Exp $
3
4/**
5 * @file
6 * Defines a widget type for content_taxonomy with autocomplete
7 **/
8
9
10/**
11 * Implementation of hook_theme().
12 */
13function content_taxonomy_autocomplete_theme() {
14  return array(
15    'content_taxonomy_autocomplete' => array(
16      'arguments' => array('element' => NULL),
17    ),
18  );
19}
20
21/**
22 * Implementation of hook_menu
23 */
24function content_taxonomy_autocomplete_menu() {
25  $items['content_taxonomy/autocomplete'] = array(
26    'title' => 'Autocomplete',
27    'page callback' => 'content_taxonomy_autocomplete_load',
28    'access arguments' => array('access content'),
29    'type' => MENU_CALLBACK
30  );
31  return $items;
32}
33
34/**
35 * Implementation of hook_widget_info().
36 */
37function content_taxonomy_autocomplete_widget_info() {
38  return array(
39    'content_taxonomy_autocomplete' => array(
40      'label' => t('Autocomplete (Freetagging)'),
41      'field types' => array('content_taxonomy'),
42      'multiple values' => CONTENT_HANDLE_MODULE,
43      'callbacks' => array(
44        'default value' => CONTENT_CALLBACK_DEFAULT,
45      ),
46    ),
47  );
48  return $items;
49}
50
51/**
52 * Implementation of hook_widget_settings
53 */
54function content_taxonomy_autocomplete_widget_settings($op, $widget) {
55  switch ($op) {
56    case 'form':
57      $form['autocomplete'] = array(
58        '#type' => 'fieldset',
59        '#title' => t('Settings for Autocompletes'),
60        '#collapsible' => TRUE,
61        '#weight' => 10,
62      );
63      $form['autocomplete']['new_terms'] = array(
64        '#type' => 'radios',
65        '#title' => t('Freetagging settings'),
66        '#default_value' => isset($widget['new_terms']) ? $widget['new_terms'] : 'insert',
67        '#options' => array('insert' => t('Allow and insert new terms by the user into the vocabulary'),
68                            'deny' => t('Deny any new terms'),
69                            ),
70      );
71      $form['autocomplete']['extra_parent'] = array(
72        '#type' => 'select',
73        '#title' => t('Extra Parent for new terms'),
74        '#options' => _content_taxonomy_get_all_terms(),
75        '#default_value' => (isset($widget['extra_parent']) && is_numeric($widget['extra_parent'])) ? $widget['extra_parent'] : 0,
76        '#description' => t('This setting is only relevant if you have selected "Allow and insert new terms by the user into the vocabulary". If you select any term here, new terms will get children of the selected one, otherwise new terms get children of the parent term (root, if no parent selected) selected in the global settings.'),
77      );
78     
79      $form['autocomplete']['maxlength'] = array(
80        '#type' => 'textfield',
81        '#title' => t('Maximum length of autocomplete'),
82        '#default_value' => (isset($widget['maxlength']) && is_numeric($widget['maxlength'])) ? $widget['maxlength'] : 255,
83        '#element_validate' => array('_content_taxonomy_autocomplete_widget_settings_maxlength_validate'),
84        '#required' => TRUE,
85        '#description' => t('Defines how many characters can be typed into the autocomplete field. For values higher than 255, remember that one term name can not be longer than 255 (would be cutted), nevertheless it\'s not a problem for multiple values, separated by commas.'),
86      );
87      if (module_exists('active_tags')) {
88        $form['autocomplete']['active_tags'] = array(
89          '#type' => 'checkbox',
90          '#title' => t('Use Active Tags style widget'),
91          '#default_value' => isset($widget['active_tags']) ? $widget['active_tags'] : 0,
92          '#description' => t('Use the Active Tags module to improve the usability of this autocomplete widget.'),
93        );
94      }
95      return $form;
96
97    case 'save':
98      return array('new_terms', 'extra_parent', 'maxlength', 'active_tags');
99  }
100}
101
102function _content_taxonomy_autocomplete_widget_settings_maxlength_validate($element, &$form_state) {
103  $value = $form_state['values']['maxlength'];
104  if (!is_numeric($value) || intval($value) != $value || $value <= 0) {
105    form_error($element, t('"Maximum length" must be a positive integer.'));
106  }
107}
108
109/**
110 * Implementation of FAPI hook_elements().
111 *
112 * Any FAPI callbacks needed for individual widgets can be declared here,
113 * and the element will be passed to those callbacks for processing.
114 *
115 * Drupal will automatically theme the element using a theme with
116 * the same name as the hook_elements key.
117 *
118 * Autocomplete_path is not used by text_widget but other widgets can use it
119 * (see nodereference and userreference).
120 */
121function content_taxonomy_autocomplete_elements() {
122  return array(
123    'content_taxonomy_autocomplete' => array(
124      '#input' => TRUE,
125      '#columns' => array('value'),
126      '#delta' => 0,
127      '#process' => array('content_taxonomy_autocomplete_process'),
128      '#autocomplete_path' => FALSE,
129      ),
130    );
131}
132
133/**
134 * Implementation of hook_widget().
135 */
136function content_taxonomy_autocomplete_widget(&$form, &$form_state, $field, $items, $delta = NULL) {
137  $element = array(
138    '#type' => 'content_taxonomy_autocomplete',
139    '#default_value' => isset($items) ? $items : NULL,
140    '#value_callback' => 'content_taxonomy_autocomplete_value',
141    '#vid' => $field['vid'],
142  );
143  return $element;
144}
145
146/**
147 * Value for a content taxonomy autocomplete field
148 *
149 * returns the taxonomy term name for term ids
150 */
151function content_taxonomy_autocomplete_value($element, $edit = FALSE) {
152  $field_key = $element['#columns'][0];
153  $terms = array();
154  if (count($element['#default_value'])) {
155    foreach ($element['#default_value'] as $delta => $entry) {
156      $terms[] = taxonomy_get_term($entry[$field_key]);
157    }
158  }
159  $value = content_taxonomy_autocomplete_merge_tags($terms, $element['#vid']);
160  $value = !empty($value) ? $value : NULL;
161  return array($field_key => $value);
162}
163
164
165/**
166 * Process an individual element.
167 *
168 * Build the form element. When creating a form using FAPI #process,
169 * note that $element['#value'] is already set.
170 *
171 */
172function content_taxonomy_autocomplete_process($element, $edit, $form_state, $form) {
173  $field_name = $element['#field_name'];
174  $field = $form['#field_info'][$field_name];
175  $field_key  = $element['#columns'][0];
176 
177  $element[$field_key] = array(
178    '#type' => 'textfield',
179    '#default_value' => isset($element['#value'][$field_key]) ? $element['#value'][$field_key] : '',
180    '#autocomplete_path' => 'content_taxonomy/autocomplete/'. $element['#field_name'],
181    '#title' => $element['#title'],
182    '#required' => $element['#required'],
183    '#description' => $element['#description'],
184    '#field_name' => $element['#field_name'],
185    '#type_name' => $element['#type_name'],
186    '#delta' => $element['#delta'],
187    '#columns' => $element['#columns'],
188    '#maxlength' => !empty($field['widget']['maxlength']) ? $field['widget']['maxlength'] : 255,
189  );
190 
191  if (empty($element[$field_key]['#element_validate'])) {
192    $element[$field_key]['#element_validate'] = array();
193  }
194  array_unshift($element[$field_key]['#element_validate'], 'content_taxonomy_autocomplete_validate');
195 
196  if (module_exists('active_tags') && $field['widget']['active_tags']) {
197    active_tags_enable_widget('#' . $element['#id'] . '-value-wrapper');
198  }
199 
200  return $element;
201}
202
203/**
204 * Validation function for the content_taxonomy_autocomplete element
205 *
206 * parses input, handles new terms (depending on settings) and sets the values as needed for storing the data
207 */
208function content_taxonomy_autocomplete_validate($element, &$form_state) {
209  $field_name = $element['#field_name'];
210  $field = content_fields($field_name, $element['#type_name']);
211  $field_key  = $element['#columns'][0];
212 
213  //if the element parents array contains the field key, we have to remove it
214  //because otherwise form_set_value won't work. (still the question why is it in)
215  if ($element['#parents'][count($element['#parents'])-1] == $field_key) {
216    array_pop($element['#parents']);
217    array_pop($element['#array_parents']);
218  }
219 
220  $value = $element['#value'];
221 
222  $extracted_ids = content_taxonomy_autocomplete_tags_get_tids($value, $field['vid'], content_taxonomy_field_get_parent($field), $field['widget']['extra_parent']);
223
224  if (!$field['multiple'] && count(content_taxonomy_autocomplete_split_tags($value, $field['vid'])) > 1) {
225    form_set_error($field['field_name'] .'][value', t('You can provide only one value'));
226    return;
227  }
228  else if (($field['multiple'] >= 2) && (count(content_taxonomy_autocomplete_split_tags($value, $field['vid'])) > $field['multiple'])) {
229    form_set_error($field['field_name'] .'][value', t('%name: this field cannot hold more than @count values.', array('%name' => t($field['widget']['label']), '@count' => $field['multiple'])));
230  }
231  if ($field['widget']['new_terms'] == 'deny') {
232    if (is_array($extracted_ids['non_existing_terms'])) {
233      form_set_error($field['field_name'] .'][value', t('New tags are not allowed'));
234      return;
235    }
236  }
237 
238  $values = content_taxonomy_autocomplete_form2data($extracted_ids, $field, $element);
239  form_set_value($element, $values, $form_state);
240}
241
242/**
243 * Helper function to transpose the values returned by submitting the content_taxonomy_autcomplete
244 * to the format to be stored in the field
245 */
246function content_taxonomy_autocomplete_form2data($extracted_ids, $field, $element) {
247  $existing_tids = is_array($extracted_ids['existing_tids']) ? $extracted_ids['existing_tids'] : array();
248  $new_tids = array();
249  if (is_array($extracted_ids['non_existing_terms'])) {
250    if ($field['widget']['extra_parent']) {
251      $new_tids = content_taxonomy_autocomplete_insert_tags($extracted_ids['non_existing_terms'], $field['widget']['extra_parent']);
252    }
253    else {
254      $new_tids = content_taxonomy_autocomplete_insert_tags($extracted_ids['non_existing_terms'], content_taxonomy_field_get_parent($field));         
255    }
256  }
257  return content_transpose_array_rows_cols(array($element['#columns'][0] => array_merge($existing_tids, $new_tids)));
258
259}
260
261
262/**
263 * Retrieve a pipe delimited string of autocomplete suggestions
264 *
265 * @param String Fieldname
266 * @param Integer TID of a parent (optional)
267 * @param BOOLEAN whether a multiple field or not
268 * @param STRING typed input
269 */
270function content_taxonomy_autocomplete_load($field_name, $string = '') {
271   // The user enters a comma-separated list of tags. We only autocomplete the last tag.
272  // This regexp allows the following types of user input:
273  // this, "somecmpany, llc", "and ""this"" w,o.rks", foo bar
274  $content_type_info = _content_type_info();
275  $vid = $content_type_info['fields'][$field_name]['vid'];
276  $tid = content_taxonomy_field_get_parent($content_type_info['fields'][$field_name]);
277 
278  // If the menu system has splitted the search text because of slashes, glue it back.
279  if (func_num_args() > 2) {
280    $args = func_get_args();
281    $string .= '/'. implode('/', array_slice($args, 2));
282  }
283 
284  // The user enters a comma-separated list of tags. We only autocomplete the last tag.
285  $array = drupal_explode_tags($string);
286
287  // Fetch last tag
288  $last_string = trim(array_pop($array));
289  $matches = array();
290  if ($last_string != '') {
291    if ($tid) {
292      $result = db_query_range(db_rewrite_sql("SELECT t.name FROM {term_data} t
293        LEFT JOIN {term_synonym} s ON t.tid = s.tid
294        INNER JOIN {term_hierarchy} h ON  t.tid = h.tid
295        WHERE h.parent = %d
296        AND (LOWER(t.name) LIKE LOWER('%%%s%%') OR LOWER(s.name) LIKE LOWER('%%%s%%'))", 't', 'tid'),
297        $tid,$last_string,$last_string,0,10);
298    }
299    else {
300      $result = db_query_range(db_rewrite_sql("SELECT t.name FROM {term_data} t
301        LEFT JOIN {term_synonym} s ON t.tid = s.tid
302        WHERE t.vid = %d
303        AND (LOWER(t.name) LIKE LOWER('%%%s%%') OR LOWER(s.name) LIKE LOWER('%%%s%%'))", 't', 'tid'),
304        $vid, $last_string, $last_string, 0, 10);
305    }
306    $prefix = count($array) ? '"'. implode('", "', $array) .'", ' : '';
307
308    while ($tag = db_fetch_object($result)) {
309      $n = $tag->name;
310      // Commas and quotes in terms are special cases, so encode 'em.
311      if (strpos($tag->name, ',') !== FALSE || strpos($tag->name, '"') !== FALSE) {
312        $n = '"'. str_replace('"', '""', $tag->name) .'"';
313      }
314      $matches[$prefix . $n] = check_plain($tag->name);
315    }
316  }
317
318  drupal_json($matches);
319}
320
321/**
322 * Get TIDs for freetagging tags
323 *  Free tagging vocabularies do not send their tids in the form,
324 *  so we'll detect them here and process them independently.
325 * @param $typed_input A string containing all comma separated tags. As the user typed it.
326 */
327function content_taxonomy_autocomplete_tags_get_tids($typed_input, $vid, $parent = 0, $extra_parent = 0) {
328  // This regexp allows the following types of user input:
329  // this, "somecmpany, llc", "and ""this"" w,o.rks", foo bar
330  $typed_terms = content_taxonomy_autocomplete_split_tags($typed_input);
331
332  foreach ($typed_terms as $typed_term) {
333    // If a user has escaped a term (to demonstrate that it is a group,
334    // or includes a comma or quote character), we remove the escape
335    // formatting so to save the term into the DB as the user intends.
336    $typed_term = trim(str_replace('""', '"', preg_replace('/^"(.*)"$/', '\1', $typed_term)));
337    if ($typed_term == "") { continue; }
338   
339    // See if the term exists in the chosen vocabulary
340    // and return the tid, otherwise, add a new record.
341    $possibilities = taxonomy_get_term_by_name($typed_term);
342
343    $typed_term_tid = NULL; // tid match if any.
344    foreach ($possibilities as $possibility) {
345      if ($possibility->vid == $vid) {
346        if ($parent) {
347          $parents = array();
348          $parents = taxonomy_get_parents($possibility->tid);
349          if (in_array($parent, array_keys($parents)) || in_array($extra_parent, array_keys($parents))) {
350            $result['existing_tids'][$possibility->tid] = $possibility->tid;
351            $typed_term_tid = $possibility->tid; 
352          }
353        }
354        else {
355          $result['existing_tids'][$possibility->tid] = $possibility->tid;
356          $typed_term_tid = $possibility->tid;
357        }
358      }
359    }
360
361    if (!$typed_term_tid) {
362      $result['non_existing_terms'][] = array(
363        'name' => $typed_term,
364        'vid' => $vid,
365      );
366    }
367  }
368
369  return $result;
370}
371
372/**
373 * Insert new tags
374 *
375 * @param $nid the node id
376 * @param $terms an array of all <strong>nonexisting</strong> terms.
377 * @return an array of newly inserted term ids
378 */
379function content_taxonomy_autocomplete_insert_tags($terms, $parent = NULL) {
380  foreach ($terms as $term) {
381    $edit = array('vid' => $term['vid'], 'name' => $term['name']);
382    if ($parent) $edit['parent'] = $parent;
383    $status = taxonomy_save_term($edit);
384    $saved_terms[$edit['tid']] = $edit['tid'];
385  }
386  return $saved_terms;
387}
388
389/**
390 * Helper function to split the tags
391 */
392function content_taxonomy_autocomplete_split_tags($typed_input) {
393  $regexp = '%(?:^|,\ *)("(?>[^"]*)(?>""[^"]* )*"|(?: [^",]*))%x';
394  preg_match_all($regexp, $typed_input, $matches);
395  return $matches[1];
396}
397
398/**
399 * Helper function to merge the tags, to prefill the fields when editing a node.
400 */
401function content_taxonomy_autocomplete_merge_tags($terms, $vid) {
402  $typed_terms = array();
403  if (!empty($terms)) {
404    foreach ($terms as $term) {
405      // Extract terms belonging to the vocabulary in question.
406      if ($term->vid == $vid) {
407        //if ($tid && in_array($term->tid,drupal_map_assoc(array_keys((taxonomy_get_children($tid,$vid)))))) {
408          // Commas and quotes in terms are special cases, so encode 'em.
409          $name = $term->name;
410          if (preg_match('/,/', $term->name) || preg_match('/"/', $term->name)) {
411            $name = '"'. preg_replace('/"/', '""', $name) .'"';
412          }
413          $typed_terms[] = $name;
414       // }
415      }
416    }
417  }
418  return implode(', ', $typed_terms);
419}
420
421function theme_content_taxonomy_autocomplete($element) {
422  return $element['#children'];
423}
Nota: Vea TracBrowser para ayuda de uso del navegador del repositorio.