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 | */ |
---|
12 | define('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 | */ |
---|
22 | function 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 | */ |
---|
111 | function 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 | */ |
---|
122 | function term_fields_perm() { |
---|
123 | return array('administer term fields', 'upload term files'); |
---|
124 | } |
---|
125 | |
---|
126 | /** |
---|
127 | * Implements hook_form_FORM_ID_alter(). |
---|
128 | */ |
---|
129 | function 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 | */ |
---|
187 | function 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 | */ |
---|
264 | function 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 | */ |
---|
306 | function 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 | */ |
---|
318 | function 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 | */ |
---|
332 | function 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 | */ |
---|
351 | function 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 | */ |
---|
382 | function 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 | */ |
---|
509 | function 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 | */ |
---|
568 | function 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 | */ |
---|
600 | function 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 | */ |
---|
614 | function 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 | */ |
---|
631 | function 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 | */ |
---|
648 | function 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 | */ |
---|
739 | function 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 | */ |
---|
761 | function 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 | */ |
---|
784 | function 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 | */ |
---|
816 | function 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 | */ |
---|
844 | function 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 | */ |
---|
851 | function 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 | */ |
---|
866 | function 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 | */ |
---|
880 | function 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 | */ |
---|
887 | function 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 | */ |
---|
915 | function 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 | */ |
---|
936 | function 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 | */ |
---|
959 | function 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 | */ |
---|
1007 | function 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 | */ |
---|
1016 | function 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 | */ |
---|
1079 | function 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 | */ |
---|
1098 | function 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 | */ |
---|
1109 | function 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 | */ |
---|
1147 | function 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 | */ |
---|
1167 | function 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 | */ |
---|
1189 | function 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 | */ |
---|
1213 | function 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 | */ |
---|
1224 | function 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 | |
---|
1232 | if (! 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 | |
---|