source: sipes/modules_contrib/term_fields/term_fields.module @ ef72343

stableversion-3.0
Last change on this file since ef72343 was 8337b9c, checked in by lhernandez <lhernandez@…>, 8 años ago

se agrego el modulo de campos para la taxonomia

  • Propiedad mode establecida a 100644
File size: 38.2 KB
Línea 
1<?php
2/**
3 * @file
4 * Allows to define and manage fields for taxonomy terms.
5 */
6
7/**
8 * Allowed tags in fields.
9 *
10 * @todo add settings page to configure that and store with variable_set()... ?
11 */
12define('TERM_FIELDS_ALLOWED_TAGS', 'a|b|big|code|del|em|i|ins|pre|q|small|span|strong|sub|sup|tt|ol|ul|li|p|br|img');
13
14/**
15 * @defgroup Drupal hooks
16 * @{
17 */
18
19/**
20 * Implements hook_menu().
21 */
22function term_fields_menu() {
23  $items = array();
24
25  // Administration items: default elements are automatically added. See below.
26
27  $items['admin/content/taxonomy/term_fields'] = array(
28    'title' => 'Fields',
29    'page callback' => 'term_fields_admin_overview',
30    'description' => 'An overview of term fields.',
31    'type' => MENU_LOCAL_TASK,
32    'weight' => 10,
33  );
34
35  $items['admin/content/taxonomy/term_fields/overview'] = array(
36    'title' => 'Overview',
37    'type' => MENU_DEFAULT_LOCAL_TASK,
38    'weight' => -10,
39  );
40
41  foreach (array_keys(taxonomy_get_vocabularies()) as $vid) {
42    $items['admin/content/taxonomy/term_fields/'. $vid] = array(
43      // This avoids to rebuild the menu on vocabulary update operation, to
44      // catch any change in vocabulary title.
45      // But because menu_local_tasks() uses the $map variable attached to
46      // the default task path instead of one built from each local item, we need
47      // to store the vocabulary ID and not the argument position as title arguments.
48      'title callback' => 'term_fields_vocabulary_title',
49      'title arguments' => array((string) $vid),
50      'page arguments' => array('term_fields_vid_overview_form', 4),
51      'description' => 'An overview of term fields.',
52      'type' => MENU_LOCAL_TASK,
53    );
54  }
55
56  $items['admin/content/taxonomy/term_fields/add'] = array(
57    'title' => 'Add field',
58    'page arguments' => array('term_fields_admin_add_term_form'),
59    'description' => 'Add a new term field.',
60    'type' => MENU_LOCAL_TASK,
61    'tab_parent' => 'admin/content/taxonomy',
62    'weight' => 11,
63  );
64
65  $items['admin/content/taxonomy/term_fields/edit/%term_fields_field/%taxonomy_vocabulary'] = array(
66    'title callback' => 'term_fields_admin_type_title',
67    'title arguments' => array(5, 6),
68    'page arguments' => array('term_fields_admin_field_configure_form', 5, 6),
69    'description' => 'Configure term field settings.',
70  );
71
72  $items['admin/content/taxonomy/term_fields/delete/%term_fields_field'] = array(
73    'title' => 'Delete field',
74    'page arguments' => array('term_fields_admin_delete_confirm_form', 5),
75    'description' => 'Delete whole term field.',
76  );
77
78  // Path for vocabulary field deletion is intentionally different from global
79  // field deletion.
80  $items['admin/content/taxonomy/term_fields/%taxonomy_vocabulary/delete/%term_fields_field'] = array(
81    'title' => 'Delete field',
82    'page arguments' => array('term_fields_admin_delete_confirm_form', 6, 4),
83    'description' => 'Delete term field for a specific vocabulary.',
84  );
85
86  // Merge administration items with own default elements.
87  $items = array_map(
88    create_function('$info', "return array_merge(
89      array(
90        'page callback' => 'drupal_get_form',
91        'access arguments' => array('administer term fields'),
92        'file' => 'term_fields.admin.inc',
93        'type' => MENU_CALLBACK),
94      \$info);"),
95    $items);
96
97  $items['term-fields/ahah/file/%term_fields_field'] = array(
98    'page callback' => 'term_fields_ahah_file',
99    'page arguments' => array(3),
100    'access arguments' => array('upload term files'),
101    'type' => MENU_CALLBACK,
102    'file' => 'plugins/file.inc',
103  );
104
105  return $items;
106}
107
108/**
109 * Implements hook_theme().
110 */
111function term_fields_theme() {
112  return array(
113    'term_fields_vid_overview_form' => array(
114      'arguments' => array('form' => NULL),
115    ),
116  );
117}
118
119/**
120 * Implements hook_perm().
121 */
122function term_fields_perm() {
123  return array('administer term fields', 'upload term files');
124}
125
126/**
127 * Implements hook_form_FORM_ID_alter().
128 */
129function term_fields_form_taxonomy_form_term_alter(&$form, &$form_state) {
130  // Checks for confirmation forms.
131  if (isset($form_state['confirm_delete']) || isset($form_state['confirm_parents'])) {
132    return;
133  }
134
135  if ($fields = term_fields_vocabulary_fields($form['vid']['#value'])) {
136    $form['#validate'][] = 'term_fields_taxonomy_form_term_validate';
137
138    $form['fields'] = array(
139      '#type' => 'fieldset',
140      '#title' => t('Term fields'),
141      '#collapsed' => TRUE,
142      '#collapsible' => TRUE,
143      '#tree' => TRUE,
144    );
145
146    $values = isset($form['#term']['tid']) ? term_fields_get_fields_values((object) $form['#term']) : array();
147
148    // Stores fields in $form_state variable if some field form elements
149    // use custom validation callbacks.
150    $form_state['#fields'] = $fields;
151
152    foreach ($fields as $field) {
153      if (module_hook($field->module, 'term_fields_forms_api')) {
154        $hook = $field->module .'_term_fields_forms_api';
155
156        if ($sub_form = call_user_func_array($hook, array('field form', &$form, &$form_state, $field, $values))) {
157          $form['fields'][$field->fid] = $sub_form;
158
159          // Actually fields are retrieved from database in the correct order, so setting
160          // field weight is not really mandatory.
161          $form['fields'][$field->fid]['#weight'] = $field->weight;
162        }
163      }
164    }
165
166    // Modify the main fields order.
167    // @todo add an option in settings page.
168    foreach (array('advanced', 'submit', 'delete') as $name) {
169      if (isset($form[$name])) {
170        $temp = $form[$name];
171        unset($form[$name]);
172        $form[$name] = $temp;
173      }
174    }
175
176    if (!isset($form['#submit'])) {
177      $form['#submit'] = array('taxonomy_form_term_submit');
178    }
179
180    array_unshift($form['#submit'], 'term_fields_taxonomy_form_term_submit');
181  }
182}
183
184/**
185 * Implements hook_taxonomy().
186 */
187function term_fields_taxonomy($op, $type, $array = NULL) {
188  switch ($type) {
189    case 'term':
190      if ($op === 'delete') {
191        db_query("DELETE FROM {term_fields_term} WHERE tid = %d", $array['tid']);
192      }
193      else {
194        // When moving terms in a vocabulary tree, each modified term is saved without
195        // its fields. @see http://drupal.org/node/1110388.
196        if (! array_key_exists('fields', $array)) {
197          return;
198        }
199
200        if ($fields = term_fields_vocabulary_fields($array['vid'])) {
201          $save = array();
202
203          foreach ($fields as $field) {
204            $values = ! empty($array['fields'][$field->fid]) ? $array['fields'][$field->fid] : array();
205
206            // Add the full $array content in a special key.
207            $values['#term'] = (object) $array;
208
209            if ($field_values = module_invoke($field->module, 'term_fields_api', 'field save', $field, $values)) {
210              // @todo intersect with database info.
211              $save += $field_values;
212            }
213          }
214
215          if ($save) {
216            $save['tid'] = $array['tid'];
217            $save['vid'] = $array['vid'];
218            $update = array();
219
220            if ($result = db_result(db_query("SELECT tid FROM {term_fields_term} WHERE tid = %d", $array['tid']))) {
221              $update = array('tid', 'vid');
222            }
223
224            // CCK has its own implementation of drupal_write_record(), which take care
225            // of NULL values.
226            if (module_exists('content') && function_exists('content_write_record')) {
227              content_write_record('term_fields_term', $save, $update);
228            }
229            else {
230              drupal_write_record('term_fields_term', $save, $update);
231
232              // Handles NULL values, because drupal_write_record() ignores them.
233              if ($update = array_filter(array_diff_key($save, array('tid' => TRUE, 'vid' => TRUE)), 'is_null')) {
234                $nulls = array();
235                $args = array($save['tid'], $save['vid']);
236
237                foreach (array_keys($update) as $column) {
238                  $nulls[] = "$column = null";
239                }
240
241                db_query("UPDATE {term_fields_term} SET ". implode(', ', $nulls) ." WHERE tid = %d AND vid = %d", $args);
242              }
243            }
244          }
245        }
246      }
247      break;
248
249    case 'vocabulary':
250      if ($op === 'delete') {
251        db_query("DELETE FROM {term_fields_instance} WHERE vid = %d", $array['vid']);
252      }
253
254      if (in_array($op, array('insert', 'delete'))) {
255        menu_rebuild();
256      }
257      break;
258  }
259}
260
261/**
262 * Implementats hook_nodeapi().
263 */
264function term_fields_nodeapi(&$node, $op, $teaser = NULL, $page = NULL) {
265  if (in_array($op, array('view', 'prepare', 'print')) && ! empty($node->taxonomy)) {
266
267    // When previewing, we can't modify $node->taxonomy, otherwise
268    // it will interfere with taxonomy_link() function, which is supposed
269    // to receive term tids and not term objects.
270    // That's why we use hook_link_alter(), which is called by taxonomy_link()
271    // function in its very end, to recover a normally filled $node->taxonomy
272    // array. So all should work fine if any term field piece of information
273    // is used in a node template file (or in a phptemplate_preprocess_node
274    // function like, which is a better way to interfere with templates).
275    if (isset($node->build_mode) && $node->build_mode == NODE_BUILD_PREVIEW) {
276
277      // If previewing, the terms must be converted to objects first.
278      $node->taxonomy_preview = taxonomy_preview_terms($node);
279      $taxonomy = &$node->taxonomy_preview;
280    }
281    else {
282      $taxonomy = &$node->taxonomy;
283    }
284
285    foreach ($taxonomy as &$term) {
286      if (is_object($term)) {
287        $term->fields = term_fields_render_fields($term);
288      }
289    }
290
291    // Clear reference.
292    unset($taxonomy);
293  }
294}
295
296/**
297 * Implements hook_link_alter().
298 *
299 * We use this hook in a non-standard way. Actually it permits to use
300 * the extra information added by term fields module to $node->taxonomy
301 * in preview mode.
302 * We would prefer to do that differently, but it don't seem to be possible
303 * without patching taxonomy module (taxonomy_link() function), which is a
304 * worse method.
305 */
306function term_fields_link_alter(&$links, $node) {
307  if (isset($node->taxonomy_preview)) {
308    // Use array_merge_recursive in case of some other modules deals with
309    // $node->taxonomy.
310    $node->taxonomy = array_merge_recursive($node->taxonomy_preview, $node->taxonomy);
311    unset($node->taxonomy_preview);
312  }
313}
314
315/**
316 * Implements hook_schema_alter()
317 */
318function term_fields_schema_alter(&$schema) {
319  $schema['term_fields_term']['fields'] = array_merge($schema['term_fields_term']['fields'], term_fields_get_storage());
320}
321
322/**
323 * @} End of "defgroup Drupal hooks".
324 *
325 * @defgroup Filefield hooks
326 * @{
327 */
328
329/**
330 * Implements hook_file_delete().
331 */
332function term_fields_file_delete($file) {
333  if ($storage = term_fields_get_storage()) {
334    foreach (term_fields_get_fields('fields') as $field => $info) {
335      if ($field->type === 'file' && isset($storage[$field->fid][$field->fid .'_fid'])) {
336        $update = array();
337
338        foreach (array_keys($storage[$field->fid]) as $column) {
339          $update[] = $column .' = null';
340        }
341
342        db_query("UPDATE {term_fields_term} SET ". implode(', ', $update) ." WHERE ". $field->fid ."_fid = %d", $file->fid);
343      }
344    }
345  }
346}
347
348/**
349 * Implements hook_file_references().
350 */
351function term_fields_file_references($file) {
352  $count = 0;
353
354  if ($storage = term_fields_get_storage()) {
355    foreach (term_fields_get_fields('fields') as $field => $info) {
356      if ($field->type === 'file' && isset($storage[$field->fid][$field->fid .'_fid'])) {
357        $count += db_result(db_query("SELECT COUNT(*) FROM {term_fields_term} WHERE ". $field->fid ."_fid' = %d", $file->fid));
358      }
359    }
360  }
361
362  return $count ? array('term_fields' => $count) : NULL;
363}
364
365/**
366 * @} End of "defgroup Filefield hooks".
367 *
368 * @defgroup Term Fields hooks
369 * @{
370 */
371
372/**
373 * Implements hook_term_fields_info().
374 *
375 * Declares field types with required information. 'title' and'description'
376 * are mandatory, 'widget' may be omitted (if so, a widget with the same name as field
377 * type is automatically added).
378 *
379 * All text parts should not be translated, because they need to be store untranslated
380 * in the {cache} table.
381 */
382function term_fields_term_fields_info() {
383  $info = array(
384    'text' => array(
385      'title' => 'Text',
386      'description' => 'Text fields may be a simple text entry or a text area with optionally text processing options.',
387      'widgets' => array(
388        'textfield' => array('title' => 'Simple text field'),
389        'textarea' => array('title' => 'Text area'),
390      ),
391    ),
392    'list' => array(
393      'title' => 'List',
394      'description' => 'A list allows to choose a value from the defined ones.',
395      'widgets' => array(
396        'radio' => array('title' => 'Radio buttons'),
397        'select' => array('title' => 'Select list'),
398      ),
399    ),
400    'choice' => array(
401      'title' => 'Single choice',
402      'description' => 'This field type exposes widgets for single choices, like active/inactive, 0/1 or yes/no questions.',
403      'widgets' => array(
404        'radio' => array('title' => 'Radio buttons'),
405        'checkbox' => array('title' => 'Checkbox'),
406      ),
407    ),
408    'numeric' => array(
409      'title' => 'Numeric',
410      'description' => 'Numeric fields store numbers and offers validation filters.',
411      'widgets' => array(
412        'textfield' => array(
413          'title' => 'Text field',
414          'description' => 'This widget accepts various numeric formats.',
415        ),
416        'select' => array(
417          'title' => 'Select list',
418          'description' => 'This widget handles only integers.',
419        ),
420      ),
421    ),
422    'file' => array(
423      'title' => 'File',
424      'description' => 'Upload files to the server.',
425      'widgets' => array(
426        'file' => array(
427          'title' => 'Generic',
428          'description' => 'This widget handles all kind of files and exposes some simple options.',
429        ),
430        'image' => array(
431          'title' => 'Image',
432          'description' => 'This widget handles images, with advanced options.',
433        ),
434      ),
435    ),
436  );
437
438  if (module_exists('date_api')) {
439    $info['date'] = array(
440     'title' => 'Date',
441      'description' => 'Handles date fields with configurable granularity and format.',
442      'widgets' => array(
443        DATE_ISO => array(
444          'title' => 'ISO date',
445          'description' => 'Store a date in the database as an ISO date, recommended for historical or partial dates.',
446        ),
447        DATE_DATETIME => array(
448          'title' => 'Datetime',
449          'description' => 'Store a date in the database as a datetime field, recommended for complete dates and times that may need timezone conversion.',
450        ),
451      ),
452    );
453
454    if (module_exists('date_popup')) {
455      $info['date']['widgets']['date_popup'] = array(
456        'title' => 'Date popup',
457        'description' => 'Use a date popup widget to select the date. Maximum granularity is year/month/day.',
458      );
459    }
460  }
461
462  // Color support.
463  $colorpicker = module_exists('colopicker') && function_exists('colorpicker_2_or_later');
464  $jquery_colorpicker = module_exists('jquery_colorpicker');
465
466  if ($colorpicker || $jquery_colorpicker) {
467    $info['color'] = array(
468     'title' => 'Color',
469      'description' => 'Exposes various color selectors.',
470      'widgets' => array(),
471    );
472
473    if ($colorpicker) {
474      $info['color']['widgets']['colorpicker'] = array(
475        'title' => 'jQuery colorpicker selector',
476        'description' => 'This widget relies on the colorpicker module.',
477      );
478    }
479
480    if ($jquery_colorpicker) {
481      $info['color']['widgets']['jquery_colorpicker'] = array(
482        'title' => 'Simple color selector',
483        'description' => 'This widget relies on the jQuery colorpicker module.',
484      );
485    }
486  }
487
488  return $info;
489}
490
491/**
492 * Implements hook_term_fields_api().
493 *
494 * @param $op
495 *   The operation to perform. It may be:
496 *   - 'storage': The column names and definition for field storage (see hook_schema).
497 *     The main column (the one that stores the value) should be returned with first
498 *     index, for Feeds compatibility (needs some work there).
499 *   - 'views': The Views data.
500 *   - 'field save': The field to save, when a term is being saved.
501 *   - 'field display': Render HTML. The term object is present in the $values array,
502 *     as '#term' key.
503 * @param $field
504 *   The field definition.
505 * @param $values
506 *   All fields or form values if applicable. For the 'field save' operation, a special
507 *   key '#term' is added to the $value array, containing the term object.
508 */
509function term_fields_term_fields_api($op, $field, $values = array()) {
510  // This allows us to call the same function either for term_fields_term_fields_api()
511  // and term_fields_term_fields_forms_api() hooks. For contributed modules such thing is
512  // totally unecessary, particularly if it implements only a few number of field types.
513  $dummy_reference = NULL;
514
515  switch ($op) {
516
517    case 'storage':
518      return term_fields_invoke_field_api($op, $field, $values, $dummy_reference, $dummy_reference);
519
520    case 'views data':
521      return term_fields_invoke_field_api('views_data', $field, $values, $dummy_reference, $dummy_reference);
522
523    case 'field save':
524      return term_fields_invoke_field_api('field_save', $field, $values, $dummy_reference, $dummy_reference);
525
526    case 'field display':
527      return term_fields_invoke_field_api('field_display', $field, $values, $dummy_reference, $dummy_reference);
528
529    case 'delete field':
530      return term_fields_invoke_field_api('field_delete', $field, $values, $dummy_reference, $dummy_reference);
531  }
532}
533
534/**
535 * Implements hook_term_fields_forms_api().
536 *
537 * @param $op
538 *   The operation to perform:
539 *   - 'settings form': The settings form for configuring the field.
540 *   - 'settings validate': Settings form validation. Note that $values variable is not set
541 *     because all data already are in the $field object.
542 *   - 'settings save': Allows to modify the form_state object before database storage. The
543 *     $field object is built from the $form_state['values'] content, so it could be the  place
544 *     where handle some cleaning, especially in the 'options' array, to reduce storage space.
545 *   - 'field form': The field form that is displayed on the term edit page.
546 *   - 'field validate': Term field values validation.
547 * @param &$form
548 *   This parameter is passed by reference to allow changes (enctype attribute, validation
549 *   function...). It may represent:
550 *   - 'settings': The global form.
551 *   - 'form': The main term edit form.
552 *   For other operations, it is not currently used.
553 * @param &$form_state
554 *    Same idea than &$form variable, see above.
555 * @param $field
556 *   The field object.
557 * @param $values
558 *   This is different for each operation:
559 *   - 'settings form': Not applicable.
560 *   - 'settings validate': The $form_state['values']['options'] data, with an additional
561 *     key '#parents' for setting error on form elements.
562 *   - 'settings save': The $form_state['values']['options'] data.
563 *   - 'field form': All term fields values for a given term.
564 *   - 'field validate': Posted values specific to the considered field. A key ['#parents']
565 *     is added, in order to easily locate the field element in the form, for example to
566 *     permit to find the error element for targeting errors.
567 */
568function term_fields_term_fields_forms_api($op, &$form, &$form_state, $field, $values = array()) {
569  switch ($op) {
570
571    case 'settings form':
572      return term_fields_invoke_field_api('settings_form', $field, $values, $form, $form_state);
573      break;
574
575    case 'settings validate':
576      term_fields_invoke_field_api('settings_form_validate', $field, $values, $form, $form_state);
577      break;
578
579    case 'settings save':
580      term_fields_invoke_field_api('settings_form_submit', $field, $values, $form, $form_state);
581      break;
582
583    case 'field form':
584      return term_fields_invoke_field_api('field_form', $field, $values, $form, $form_state);
585
586    case 'field validate':
587      term_fields_invoke_field_api('field_form_validate', $field, $values, $form, $form_state);
588      break;
589  }
590}
591
592/**
593 * Implements hook_term_fields_suffixes().
594 *
595 * @return
596 *   An array containing the suffixes that may be used in the {term_fields_term} column
597 *   names for all of the fields defined by this module. This avoids collision with user
598 *   defined field IDs.
599 */
600function term_fields_term_fields_suffixes() {
601  return array('_value', '_format', '_fid', '_alt');
602}
603
604/**
605 * @} End of "defgroup Term Fields hooks".
606 *
607 * @defgroup I18N hooks
608 * @{
609 */
610
611/**
612* Implements hook_locale().
613*/
614function term_fields_locale($op = 'groups', $group = NULL) {
615  switch ($op) {
616    case 'groups':
617      return array('term_fields' => t('Term fields'));
618  }
619}
620
621/**
622 * @} End of "defgroup I18N hooks".
623 *
624 * @defgroup Views hooks
625 * @{
626 */
627
628/**
629 * Implements hook_views_api().
630 */
631function term_fields_views_api() {
632  return array(
633    'api' => 2,
634    'path' => drupal_get_path('module', 'term_fields') . '/views',
635  );
636}
637
638/**
639 * @} End of "defgroup Views hooks".
640 *
641 * @defgroup Token hooks
642 * @{
643 */
644
645/**
646 * Implements hook_token_values().
647 */
648function term_fields_token_values($type, $object = NULL, $options = array()) {
649  $tokens = $terms = array();
650
651  // When dealing with node, we have to check if the term fields have
652  // already benn loaded (nodeapi operations: 'view', 'presave', 'print').
653  // If not (operation 'presave'), we have to buils the fields array for
654  // each field.
655  if ($type === 'node' && ! (empty($object->taxonomy))) {
656    $check = reset($object->taxonomy);
657
658    // Fields have been loaded.
659    if (is_object($check)) {
660      $terms = array_map(create_function('$term', 'return isset($term->fields) ? $term->fields : array();'), $object->taxonomy);
661    }
662    // Nodeapi presave operation. This case is important, especially if the
663    // Pathauto module is used.
664    else {
665      // Tags
666      if (isset($object->taxonomy['tags'])) {
667        $tags = $object->taxonomy['tags'];
668        unset($object->taxonomy['tags']);
669
670        foreach ($tags as $vid => $tag) {
671          $names = drupal_explode_tags($tag);
672
673          foreach ($names as $name) {
674            $tid = NULL;
675
676            foreach (taxonomy_get_term_by_name($name) as $term) {
677              if ($term->vid == $vid) {
678                $tid = $term->tid;
679                break;
680              }
681            }
682
683            if ($tid && ! isset($terms[$tid])) {
684              $terms[$tid] = term_fields_render_fields($term);
685
686            }
687          }
688        }
689      }
690
691      // Vocabularies
692      foreach ($object->taxonomy as $vid => $term) {
693        if (is_array($term)) {
694          foreach ($term as $tid) {
695            if ($tid && ! isset($terms[$tid])) {
696              $dummy_term = (object) array('tid' => $tid, 'vid' => $vid);
697              $terms[$tid] = term_fields_render_fields($dummy_term);
698            }
699          }
700        }
701        elseif (is_object($term)) {
702          if (! isset($terms[$term->tid])) {
703            $terms[$term->tid] = term_fields_render_fields($term);
704          }
705        }
706        elseif ($term) {
707          $dummy_term = (object) array('tid' => $term, 'vid' => $vid);
708          $terms[$term] = term_fields_render_fields($dummy_term);
709        }
710      }
711    }
712  }
713  // Taxonomy
714  elseif ($type === 'taxonomy') {
715    $terms[$object->tid] = term_fields_render_fields($object);
716  }
717
718  if (in_array($type, array('taxonomy', 'node'), TRUE)) {
719    foreach (term_fields_get_fields('fields') as $fid => $field) {
720      $value = '';
721
722      if ($terms) {
723        $function = create_function('$term', 'return isset($term["'. $fid .'"]) ? $term["'. $fid .'"] : NULL;');
724        if ($values = array_filter(array_map($function, $terms))) {
725          $value = implode(',', $values);
726        }
727      }
728
729      $tokens["field-$fid"] = $value;
730    }
731  }
732
733  return $tokens;
734}
735
736/**
737 * Implements hook_token_list().
738 */
739function term_fields_token_list($type = 'all') {
740  if ($type == 'taxonomy' || $type == 'node' || $type == 'all') {
741    $tokens = array();
742
743    foreach (term_fields_get_fields('fields') as $fid => $field) {
744      $tokens['term_fields']["field-$fid"] = t('@title', array('@title' => $field->title));
745    }
746
747    return $tokens;
748  }
749}
750
751/**
752 * @} End of "defgroup Token hooks".
753 *
754 * @defgroup cTools hooks
755 * @{
756 */
757
758/**
759 * Implementation hook_ctools_plugin_api()
760 */
761function term_fields_ctools_plugin_api($owner, $api) {
762  if ($owner == 'feeds' && $api == 'plugins') {
763    return array(
764      'version' => 1,
765      'path' => drupal_get_path('module', 'term_fields') . '/feeds',
766    );
767  }
768}
769
770/**
771 * @} End of "defgroup cTools hooks".
772 *
773 * @defgroup Feeds hooks
774 * @{
775 */
776
777/**
778 * A hook_feeds_plugins() declares available Fetcher, Parser or Processor
779 * plugins to Feeds. For an example look at feeds_feeds_plugin(). For exposing
780 * this hook hook_ctools_plugin_api() MUST be implemented, too.
781 *
782 * @see feeds_feeds_plugin()
783 */
784function term_fields_feeds_plugins() {
785  $info = array();
786
787  $info['TermFieldsTermProcessor'] = array(
788    'name' => 'Taxonomy term processor (with term fields)',
789    'description' => 'Create taxonomy terms from parsed content. This is a extended Taxonomy term processor.',
790    // 'help' => 'More verbose description here. Will be displayed on processor selection menu.',
791    'handler' => array(
792      'parent' => 'FeedsTermProcessor',
793      'class' => 'TermFieldsTermProcessor',
794      'file' => 'TermFieldsTermProcessor.inc',
795      'path' => drupal_get_path('module', 'term_fields') . '/feeds',
796    ),
797  );
798
799  return $info;
800}
801
802/**
803 * Alter mapping targets for taxonomy terms. Use this hook to add additional
804 * target options to the mapping form of Taxonomy term processor.
805 *
806 * For an example implementation, look at geotaxonomy module.
807 * http://drupal.org/project/geotaxonomy
808 *
809 * @param &$targets
810 *   Array containing the targets to be offered to the user. Add to this array
811 *   to expose additional options. Remove from this array to suppress options.
812 *   Remove with caution.
813 * @param $vid
814 *   The vocabulary id
815 */
816function term_fields_feeds_term_processor_targets_alter(&$targets, $vid) {
817  // @todo actually I think that we should map each table column and
818  // provide more options. If anyone still need Feeds integration, please
819  // create a new issue for advanced Feeds support. Help for testing is
820  // very welcome!
821
822  foreach(term_fields_get_fields('fields') as $field) {
823    // Ignore file fields, that need special handling.
824    if ($field->type == 'file') {
825      continue;
826    }
827
828    $targets[$field->fid] = array(
829      'name' => 'Term field: ' . $field->title,
830      'description' => $field->description,
831      'optional_unique' => TRUE,
832      'callback' => 'term_fields_feeds_term_set_target'
833    );
834  }
835}
836
837/**
838 * Callback for mapping. Here is where the actual mapping happens.
839 *
840 * When the callback is invoked, $target contains the name of the field the
841 * user has decided to map to and $value contains the value of the feed item
842 * element the user has picked as a source.
843 */
844function term_fields_feeds_term_set_target(&$term, $target, $value) {
845  $term['fields'][$target]['value'] = $value;
846}
847
848/**
849 * Helper function. Load a taxonomy term and merge with term fields.
850 */
851function term_fields_feeds_taxonomy_load($term) {
852  $term_object = (object) $term;
853  $fields = term_fields_render_fields($term_object);
854  return array_merge($term, $fields);
855}
856
857/**
858 * @} End of "defgroup Feeds hooks".
859 */
860
861/**
862 * Wrapper for translation through I18N strings.
863 *
864 * @see i18nstrings()
865 */
866function term_fields_translate($name, $string, $field = NULL, $langcode = NULL, $textgroup = 'term_fields') {
867  $key = array($textgroup);
868
869  if (! empty($field->fid)) {
870    $key[] = $field->fid;
871  }
872
873  $key[] = $name;
874  return function_exists('i18nstrings') ? i18nstrings(implode(':', $key), $string, $langcode) : $string;
875}
876
877/**
878 * Gets a standard formatted title for a Views table.
879 */
880function term_fields_views_format_title($field) {
881  return t('@title (@fid)', array('@title' => $field->title, '@fid' => $field->fid));
882}
883
884/**
885 * Gets a standard formatted help for a Views table.
886 */
887function term_fields_views_format_help($field) {
888  $help = t($field->type) .'/'. t($field->widget) .' - ';
889
890  if (! empty($field->vids)) {
891    $vocs = array();
892
893    foreach ($field->vids as $voc) {
894      $vocabulary = taxonomy_vocabulary_load($voc['vid']);
895      $vocs[] = check_plain($vocabulary->name);
896    }
897
898    $help .= t('Appears in: @vocabularies.', array('@vocabularies' => implode(', ', $vocs)));
899  }
900  else {
901    $help .= t('Not used in any vocabulary yet.');
902  }
903
904  return $help;
905}
906
907/**
908 * Gets a list of field instances for a given vocabulary.
909 *
910 * @param $vid
911 *   The vocabulary ID.
912 * @return
913 *   An array of field definitions.
914 */
915function term_fields_vocabulary_fields($vid = NULL) {
916  static $vids = array();
917
918  if( ! isset($vids[$vid])) {
919    $fields = term_fields_get_fields();
920    $vids[$vid] = array();
921
922    if (array_key_exists($vid, $fields)) {
923      array_walk($fields[$vid], 'term_fields_walk_vocabulary_fields', $vid);
924      $vids[$vid] = $fields[$vid];
925    }
926  }
927
928  return $vids[$vid];
929}
930
931/**
932 * Callback function for array_walk().
933 *
934 * @see term_fields_vocabulary_fields().
935 */
936function term_fields_walk_vocabulary_fields(&$field, $key, $vid) {
937  // Not really necessay, just paranoïd...
938  $field->vids[$vid] += array('vid' => $vid, 'required' => FALSE, 'weight' => 0);
939
940  foreach ($field->vids[$vid] as $name => $value) {
941    $field->{$name} = $value;
942  }
943}
944
945/**
946 * Gets a list of field instances.
947 *
948 * @param $key
949 *   The type of . Key may be:
950 *   - 'fields': The list of all fields, indexed by field ID.
951 *   - 'voc': The list of all fields aatached to at least one vocabulary,
952 *     indexed by vocabulary ID (vid).
953 *   With any other value, the full $fields variables is returned.
954 * @param $reset
955 *   TRUE means clearing the fields information stored in database cache.
956 * @return
957 *   An array of field definitions.
958 */
959function term_fields_get_fields($key = 'voc', $reset = FALSE) {
960  static $fields;
961
962  if ($reset) {
963    $fields = NULL;
964    cache_clear_all('term_fields_fields', 'cache');
965  }
966
967  if (! isset($fields)) {
968    if ($cache = cache_get('term_fields_fields', 'cache')) {
969      $fields = $cache->data;
970    }
971    else {
972      $fields = array('fields' => array(), 'voc' => array());
973      $result = db_query("SELECT tf.fid, tf.module, tf.title, tf.description, tf.type, tf.widget, tf.options, tf.instantiated, tfi.vid, tfi.required, tfi.weight
974        FROM {term_fields} tf LEFT JOIN {term_fields_instance} tfi ON tf.fid = tfi.fid ORDER BY tfi.vid, tfi.weight, tf.title");
975
976      while ($field = db_fetch_object($result)) {
977        foreach (array('vid', 'required', 'weight') as $name) {
978          ${$name} = $field->{$name};
979          unset($field->{$name});
980        }
981
982        if (! isset($fields['fields'][$field->fid])) {
983          $field->vids = array();
984          $field->options = ! empty($field->options) ? unserialize($field->options) : array();
985          $fields['fields'][$field->fid] = drupal_clone($field);
986        }
987
988        if (isset($vid) && is_numeric($vid)) {
989          // Use reference instead of copying object, to reduce memory usage.
990          // @todo compare memory gain vs process cost, because this forces extra
991          // process in term_fields_vocabulary_fields() function...
992          $fields['voc'][$vid][$field->fid] = & $fields['fields'][$field->fid];
993          $fields['fields'][$field->fid]->vids[$vid] = array('vid' => $vid, 'required' => (bool) $required, 'weight' => $weight);
994        }
995      }
996
997      cache_set('term_fields_fields', $fields, 'cache');
998    }
999  }
1000
1001  return array_key_exists($key, $fields) ? $fields[$key] : $fields;
1002}
1003
1004/**
1005 * Invokes the specified Term Fields plugin.
1006 */
1007function term_fields_invoke_field_api($callback, $field, $values, &$form, &$form_state) {
1008  module_load_include('inc', 'term_fields', "plugins/$field->type");
1009  $function = 'term_fields_'. $field->type .'_'. $callback;
1010  return function_exists($function) ? $function($field, $values, $form, $form_state) : array();
1011}
1012
1013/**
1014 * Retrieves information about supported fields.
1015 */
1016function term_fields_get_fields_info($type = NULL, $reset = FALSE) {
1017  static $info;
1018
1019  if ($reset) {
1020    $info = NULL;
1021    cache_clear_all('term_fields_info', 'cache');
1022  }
1023
1024  if (! isset($info)) {
1025    if ($cache = cache_get('term_fields_info', 'cache')) {
1026      $info = $cache->data;
1027    }
1028    else {
1029      $info = array();
1030
1031      foreach (module_implements('term_fields_info') as $module) {
1032        $function = $module .'_term_fields_info';
1033
1034        if ($types = $function()) {
1035          // Ensures defined types are valid.
1036          foreach ($types as $key => $type_info) {
1037            if (! strlen($key) || is_numeric($key) || ! isset($type_info['title']) || ! isset($type_info['description'])) {
1038              unset($types[$key]);
1039              continue;
1040            }
1041
1042            if (empty($type_info['widgets'])) {
1043              $types[$key]['widgets'] = array($key => array('title' => $type_info['title']));
1044            }
1045
1046            foreach ($types[$key]['widgets'] as $w_key => $widget) {
1047              if (! array_key_exists('title', $widget)) {
1048                unset($types[$key]['widgets'][$w_key]);
1049              }
1050            }
1051
1052            $types[$key]['module'] = $module;
1053          }
1054
1055          // Do not override existing types with array_merge().
1056          $info += $types;
1057        }
1058      }
1059
1060      drupal_alter('term_fields_info', $info);
1061      cache_set('term_fields_info', $info, 'cache');
1062    }
1063  }
1064
1065  if ($type) {
1066    return array_key_exists($type, $info) ? $info[$type] : array();
1067  }
1068
1069  return $info;
1070}
1071
1072/**
1073 * Form validation handler for the taxonomy_form_term() form.
1074 *
1075 * @see taxonomy_form_term()
1076 * @see term_fields_form_taxonomy_form_term_alter()
1077 * @see term_fields_taxonomy_form_term_submit()
1078 */
1079function term_fields_taxonomy_form_term_validate(&$form, &$form_state) {
1080  if ($fields = term_fields_vocabulary_fields($form_state['values']['vid'])) {
1081    foreach ($fields as $field) {
1082      if (module_hook($field->module, 'term_fields_forms_api')) {
1083        $values = $form_state['values']['fields'][$field->fid];
1084        $values['#parents'] = array('fields', $field->fid);
1085        call_user_func_array($field->module .'_term_fields_forms_api', array('field validate', &$form, &$form_state, $field, $values));
1086      }
1087    }
1088  }
1089}
1090
1091/**
1092 * Form submission handler; txonomy term form.
1093 *
1094 * @see taxonomy_form_term()
1095 * @see term_fields_form_taxonomy_form_term_alter()
1096 * @see term_fields_taxonomy_form_term_validate()
1097 */
1098function term_fields_taxonomy_form_term_submit($form, &$form_state) {
1099  // Clear the $form_state['storage']['fields'] is not empty. It is used by the
1100  // file field AHAH.
1101  if (isset($form_state['storage']['fields'])) {
1102    unset($form_state['storage']['fields']);
1103  }
1104}
1105
1106/**
1107 * Gets the columns definition for the table {term_fields_term}.
1108 */
1109function term_fields_get_storage($vid = NULL, $reset = FALSE) {
1110  static $storage = array();
1111
1112  if ($reset) {
1113    $storage = NULL;
1114    cache_clear_all('term_fields_storage', 'cache');
1115  }
1116
1117  if (! isset($info)) {
1118    if ($cache = cache_get('term_fields_storage', 'cache')) {
1119      $storage = $cache->data;
1120    }
1121    else {
1122      $storage = array();
1123
1124      foreach (term_fields_get_fields('fields') as $field) {
1125        if ($columns = module_invoke($field->module, 'term_fields_api', 'storage', $field)) {
1126          $storage[$field->fid] = $columns;
1127        }
1128      }
1129
1130      cache_set('term_fields_storage', $storage, 'cache');
1131    }
1132  }
1133
1134  $fields = $vid ? array_intersect_key($storage, term_fields_vocabulary_fields($vid)) : $storage;
1135  $columns = array();
1136
1137  foreach ($fields as $cols) {
1138    $columns += $cols;
1139  }
1140
1141  return $columns;
1142}
1143
1144/**
1145 * Gets rendered fields for a term.
1146 */
1147function term_fields_render_fields($term) {
1148  $fields = array();
1149  $values = term_fields_get_fields_values($term);
1150  $values['#term'] = $term;
1151
1152  foreach (term_fields_vocabulary_fields($term->vid) as $fid => $field) {
1153    $fields[$fid] = module_invoke($field->module, 'term_fields_api', 'field display', $field, $values);
1154  }
1155
1156  return $fields;
1157}
1158
1159/**
1160 * Gets term fields values.
1161 *
1162 * @param $term
1163 *   The term object.
1164 * @return
1165 *   An array of fields values from a given term, keyed by field ID.
1166 */
1167function term_fields_get_fields_values($term) {
1168  if ($storage = term_fields_get_storage($term->vid)) {
1169    $columns = implode(', ', array_keys($storage));
1170
1171    // Rewriting query may allow other modules to retrieve some data in
1172    // joined tables. Do not join with any table which could return more
1173    // than one row: multiple values are handled below.
1174    $sql = db_rewrite_sql("SELECT $columns FROM {term_fields_term} tf WHERE tf.tid = %d", 'tf', 'tid', array($term->tid));
1175
1176    if ($values = db_fetch_array(db_query($sql, $term->tid))) {
1177      // Here it is possible to play with multiple values, using own query(ies).
1178      drupal_alter('term_fields_field_values', $values, $term, term_fields_vocabulary_fields($term->vid));
1179      return $values;
1180    }
1181  }
1182
1183  return array();
1184}
1185
1186/**
1187 * Loads a fields from its ID.
1188 */
1189function term_fields_field_load($fid) {
1190  if ($fid) {
1191    $fields = term_fields_get_fields('fields');
1192
1193    if (array_key_exists($fid, $fields) && is_object($fields[$fid])) {
1194
1195      // Ensures that the module that handles this type of field is
1196      // still enabled and supports the specified type and widget.
1197      if (module_exists($fields[$fid]->module)) {
1198        $field_info = term_fields_get_fields_info($fields[$fid]->type);
1199
1200        if (array_key_exists($fields[$fid]->widget, $field_info['widgets'])) {
1201          return $fields[$fid];
1202        }
1203      }
1204    }
1205  }
1206
1207  return FALSE;
1208}
1209
1210/**
1211 * Title callback.
1212 */
1213function term_fields_admin_type_title($field, $vocabulary) {
1214  // No need for isset() there, all checks have been performed by
1215  // the term_fields_field_load() function.
1216  $field_info = term_fields_get_fields_info($field->type);
1217
1218  return t('Configure the field @field of type @type attached to the vocabulary @vocabulary', array('@field' => $field->title, '@type' => t($field_info['title']), '@widget' => t($field_info['widgets'][$field->widget]['title']), '@vocabulary' => $vocabulary->name));
1219}
1220
1221/**
1222 * Title callback.
1223 */
1224function term_fields_vocabulary_title($vid) {
1225  if ($vocabulary = taxonomy_vocabulary_load($vid)) {
1226    return check_plain($vocabulary->name);
1227  }
1228
1229  return t('Undefined');
1230}
1231
1232if (! module_exists('content')) {
1233  /**
1234   * Helper form element validator : integer > 0.
1235   */
1236  function _element_validate_integer_positive($element, &$form_state) {
1237    $value = $element['#value'];
1238    if ($value !== '' && (!is_numeric($value) || intval($value) != $value || $value <= 0)) {
1239      form_error($element, t('%name must be a positive integer.', array('%name' => $element['#title'])));
1240    }
1241  }
1242}
1243
Nota: Vea TracBrowser para ayuda de uso del navegador del repositorio.