/[drupal]/contributions/modules/importexportapi/importexportapi.module
ViewVC logotype

Contents of /contributions/modules/importexportapi/importexportapi.module

Parent Directory Parent Directory | Revision Log Revision Log | View Revision Graph Revision Graph


Revision 1.53 - (show annotations) (download) (as text)
Thu Feb 8 13:30:28 2007 UTC (2 years, 9 months ago) by douggreen
Branch: MAIN
CVS Tags: DRUPAL-5--1-1, DRUPAL-5--1-0, HEAD
Branch point for: DRUPAL-5, DRUPAL-6--1
Changes since 1.52: +5 -5 lines
File MIME type: text/x-php
#117064 from victorkane - D5 api changes
1 <?php
2 // $Id: importexportapi.module,v 1.52 2007/02/07 13:19:13 douggreen Exp $
3
4 /**
5 * @file
6 * Allows data entities in Drupal to be imported and exported in a variety of
7 * formats.
8 */
9
10 /**
11 * The maximum number of times that the entity dependency checker will loop
12 * through the data definitions. This maximum limit exists in order to prevent
13 * infinite loops, which may occur if entities have been defined with cyclic
14 * dependencies.
15 */
16 define('IMPORTEXPORTAPI_DEPENDENCY_TIMEOUT', 10);
17
18 /**
19 * Implementation of hook_help().
20 */
21 function importexportapi_help($section) {
22 switch ($section) {
23 case 'admin/help#importexportapi':
24 return t('The import / export API module allows data entities in Drupal to be imported and exported in a variety of formats.');
25 }
26 }
27
28 /**
29 * Implementation of hook_init().
30 */
31 function importexportapi_init() {
32 // hook init is called even on cached pages, but we don't want to
33 // actually do anything in that case.
34 if (!function_exists('drupal_get_path')) {
35 return;
36 }
37
38 _importexportapi_include_core_defs();
39 _importexportapi_include_core_engines();
40 _importexportapi_include_core_fields();
41 }
42
43 /**
44 * Implementation of hook_def_field_types().
45 */
46 function importexportapi_def_field_types($op) {
47 $type = array();
48
49 switch ($op) {
50 case 'placeholders':
51 $type['int'] = '%d';
52 $type['float'] = '%f';
53 $type['string'] = "'%s'";
54 $type['file'] = "'%s'";
55 $type['freeform'] = "'%s'";
56 $type['serialized'] = "'%s'";
57 $type['datetime'] = "'%s'";
58 break;
59 case 'defaults':
60 $type['int'] = array('#required' => FALSE, '#default_value' => 0);
61 $type['float'] = array('#required' => FALSE, '#default_value' => 0.0);
62 $type['string'] = array('#required' => FALSE, '#default_value' => '');
63 $type['file'] = array('#required' => FALSE, '#default_value' => '');
64 $type['freeform'] = array('#required' => FALSE, '#default_value' => array(), '#process' => array('importexportapi_process_freeform' => array()));
65 $type['serialized'] = array('#required' => FALSE, '#default_value' => '', '#process' => array('importexportapi_process_serialized' => array()));
66 $type['datetime'] = array('#required' => FALSE, '#default_value' => '', '#process' => array('importexportapi_process_datetime' => array()));
67 $type['array'] = array('#process' => array('importexportapi_process_array' => array()));
68 $type['entity'] = array();
69 break;
70 }
71
72 return $type;
73 }
74
75 /**
76 * Implementation of hook_engines_get_put().
77 */
78 function importexportapi_engines_get_put() {
79 return array(
80 'db' => array(
81 'title' => t('Database'),
82 'get' => array(
83 'callback' => 'importexportapi_db_get',
84 'build' => array('importexportapi_db_build' => array('get'), 'importexportapi_db_build_references' => array('get')),
85 'build_alt_key' => array('importexportapi_db_build_alt_key' => array('get'))
86 ),
87 'put' => array(
88 'callback' => 'importexportapi_db_put',
89 'build' => array('importexportapi_db_build' => array('put'), 'importexportapi_db_build_references' => array('put')),
90 'build_alt_key' => array('importexportapi_db_build_alt_key' => array('put')),
91 'process' => array('importexportapi_db_put_resolve_alt_keys' => array())
92 )
93 ),
94 'xml' => array(
95 'title' => t('XML (eXtensible Markup Language)'),
96 'get' => array(
97 'callback' => 'importexportapi_xml_get',
98 'build' => array('importexportapi_xml_build' => array('get')),
99 'build_alt_key' => array('importexportapi_xml_build_alt_key' => array('get'))
100 ),
101 'put' => array(
102 'callback' => 'importexportapi_xml_put',
103 'build' => array('importexportapi_xml_build' => array('put')),
104 'build_alt_key' => array('importexportapi_xml_build_alt_key' => array('put'))
105 )
106 ),
107 'csv' => array(
108 'title' => t('CSV (Comma Separated Values)'),
109 'get' => array(
110 'callback' => 'importexportapi_csv_get',
111 'build' => array('importexportapi_csv_build' => array('get')),
112 'build_alt_key' => array('importexportapi_csv_build_alt_key' => array('get'))
113 ),
114 'put' => array(
115 'callback' => 'importexportapi_csv_put',
116 'build' => array('importexportapi_csv_build' => array('put')),
117 'build_alt_key' => array('importexportapi_csv_build_alt_key' => array('put'))
118 )
119 )
120 );
121 }
122
123 /**
124 * Fetches the data definition for the specified entity or field. Although the
125 * importexportapi_set_def() function can always be used instead of this one,
126 * it is recommended for code readability that you use this function, unless
127 * you actually need to set the value of a field's attributes.
128 *
129 * @param $field
130 * The field or entity for which to fetch a definition. The required format
131 * for this value is the same as that for the $field parameter in the
132 * importexportapi_set_def() function (defaults to NULL).
133 * @param $build
134 * Boolean flag indicating whether or not the data definition(s) should be
135 * built, using importexportapi_build_def(), before being returned (defaults
136 * to FALSE).
137 *
138 * @return
139 * Nested array of entity attributes and fields; or an array of all entity
140 * attributes and fields, if $field is NULL.
141 */
142 function importexportapi_get_def($field = NULL, $build = FALSE) {
143 if ($build) {
144 importexportapi_build_def();
145 }
146
147 return importexportapi_set_def($field);
148 }
149
150 /**
151 * Fetches the data definition for the specified entity or field, as defined by
152 * modules in Drupal; or fetches all data definitions for all available
153 * entities. Also allows the data definition for the specified entity or field
154 * to have its values modified.
155 *
156 * @param $field
157 * The field or entity for which to fetch a definition. This value should
158 * always be an array, but it can also be a string when specifying an entity.
159 * If the field's direct parent is not an entity, then each element should be
160 * one of the field's ancestors (in order from furthest to closest ancestor).
161 * The final element is always the field itself (defaults to NULL).
162 * @param $values
163 * The values to set for attributes of the specified field. If these values
164 * already exist, they will be overridden (for non-array values) or merged
165 * (for array values). Only set values for attributes here - do not modify
166 * child elements. Child elements should be modified by calling this function
167 * specifically for that child (defaults to an empty array).
168 * @param $refresh
169 * Whether or not to reset the internal cache of this function (defaults to
170 * FALSE).
171 *
172 * @return
173 * Nested array of entity attributes and fields; or an array of all entity
174 * attributes and fields, if $field is NULL.
175 */
176 function importexportapi_set_def($field = NULL, $values = array(), $refresh = FALSE) {
177 static $def;
178
179 if (!isset($def) || $refresh) {
180 $def = array();
181
182 // Fetch the core of the data definitions.
183 foreach (module_implements('def') as $module) {
184 $def_temp = module_invoke($module, 'def');
185 if (isset($def_temp) && is_array($def_temp)) {
186 $def = array_merge_recursive($def, $def_temp);
187 }
188 }
189
190 // Allow modules to alter the definitions that have been loaded.
191 foreach (module_implements('def_alter') as $module) {
192 $function = $module .'_def_alter';
193 $function($def);
194 }
195
196 // Take what's been provided so far, and build definitions that are ready
197 // to be passed to the 'get' and 'put' engines.
198 foreach ($def as $entity_id => $entity_data) {
199 $def[$entity_id] = _importexportapi_set_def_init($entity_id, $entity_data);
200 }
201 $def = _importexportapi_set_def_dependencies($def);
202 }
203
204 // Return a specific entity, field, or nested field.
205 if (isset($field)) {
206 if (!is_array($field)) {
207 $field = array($field);
208 }
209
210 // If the field is an array, then recurse down through the field
211 // definition.
212 return _importexportapi_set_def_values($field, $def, $values);
213 }
214 // Return all entities.
215 return $def;
216 }
217
218 /**
219 * Helper function for importexportapi_set_def(). Builds the basic attributes
220 * of an entity, to prepare it for use by other components of the system. This
221 * function recurses down from an entity, to its fields, to children of array
222 * fields.
223 *
224 * @param $field_id
225 * The name of the field to be built. When called by other functions, this is
226 * generally the name of an entity.
227 * @param $field
228 * The data to be built for the specified field. When called by other
229 * functions, this is generally the entity definition.
230 *
231 * @return
232 * The built version of $field.
233 */
234 function _importexportapi_set_def_init($field_id, $field) {
235 $field['#id'] = $field_id;
236
237 // 'string' is the default field type when none is specified.
238 if (!isset($field['#type'])) {
239 $field['#type'] = 'string';
240 }
241
242 // Use default field values for any fields that haven't been set.
243 $default_values = importexportapi_get_field_type_info($field['#type']);
244 $field += $default_values;
245 if (isset($field['#process']) && isset($default_values['#process'])) {
246 $field['#process'] = array_merge($default_values['#process'], $field['#process']);
247 }
248 if (isset($field['#alt_key_for']) && !isset($field['#unique'])) {
249 $field['#unique'] = TRUE;
250 }
251 if ((!empty($field['#key']) || !empty($field['#reference_entity'])) && $field['#type'] == 'int') {
252 $field['#default_value'] = NULL;
253 }
254
255 if ($field['#type'] == 'entity' && !isset($field['#source'])) {
256 $field['#source'] = 'db';
257 }
258 foreach (element_children($field) as $child) {
259 // Arrays inherit the same parents as those defined for their direct
260 // parent, with the addition of the direct parent itself.
261 $field[$child]['#parents'] = isset($field['#parents']) ? $field['#parents'] : array();
262 $field[$child]['#parents'][] = $field_id;
263
264 if (empty($field[$child]['#type']) || $field[$child]['#type'] != 'array') {
265 // For fields that reference other fields, if the reference table has
266 // been set, but not the reference field, then we assume that the name of
267 // the referenced field is the same as this field's name.
268 if (isset($field[$child]['#reference_entity']) && !isset($field[$child]['#reference_field'])) {
269 $field[$child]['#reference_field'] = array($child);
270 }
271 if (isset($field[$child]['#reference_entity']) && !isset($field[$child]['#reference_delta'])) {
272 $field[$child]['#reference_delta'] = 0;
273 }
274
275 if (!empty($field[$child]['#key']) || !empty($field[$child]['#key_component'])) {
276 $field['#keys'][$child] = $child;
277 }
278 }
279 }
280
281 // Recurse down through entity fields, array fields, and nested arrays.
282 foreach (element_children($field) as $child) {
283 $field[$child] = _importexportapi_set_def_init($child, $field[$child]);
284 }
285
286 return $field;
287 }
288
289 /**
290 * Helper function for importexportapi_set_def(). Determines dependencies
291 * between entities, and re-arranges the list of entities to reflect entity
292 * dependency.
293 *
294 * @param $def
295 * The data definitions for all entities.
296 *
297 * @return
298 * The re-arranged version of $def, with entity dependencies also explicitly
299 * marked.
300 */
301 function _importexportapi_set_def_dependencies($def) {
302 $def_new = array();
303
304 // Determine all dependencies for each entity.
305 foreach ($def as $entity_id => $entity_data) {
306 if (!isset($def[$entity_id]['#dependencies'])) {
307 $def[$entity_id]['#dependencies'] = array();
308 }
309 $def[$entity_id]['#dependencies'] += _importexportapi_set_def_dependencies_recurse($entity_data);
310 }
311
312 $loop_count = 0;
313 // Loop through the original list of entities, until either all the entities
314 // have been copied to $def_new, or we reach the loop timeout.
315 while (count($def) > count($def_new) && $loop_count < IMPORTEXPORTAPI_DEPENDENCY_TIMEOUT) {
316 $loop_count++;
317
318 foreach ($def as $entity_id => $entity_data) {
319 if (!isset($def_new[$entity_id])) {
320 $pending = FALSE;
321
322 // Check that all entities upon which this entity depends have already
323 // been copied to $def_new. If not, this entity becomes 'pending', and
324 // it gets copied at a later pass through the loop.
325 foreach ($entity_data['#dependencies'] as $dependent) {
326 if (!isset($def_new[$dependent]) && isset($def[$dependent])) {
327 $pending = TRUE;
328 break;
329 }
330 }
331
332 // The entity is not (or is no longer) pending, so copy it to $def_new
333 // now.
334 if (!$pending || $loop_count == IMPORTEXPORTAPI_DEPENDENCY_TIMEOUT) {
335 $def_new[$entity_id] = $entity_data;
336 }
337 }
338 }
339 }
340
341 return $def_new;
342 }
343
344 /**
345 * Helper function for _importexportapi_set_def_dependencies(). Recurses down
346 * through all fields of an entity, and finds entity dependencies.
347 *
348 * @param $field
349 * The entity or field to recurse down into.
350 *
351 * @return
352 * An array listing the dependent entities found.
353 */
354 function _importexportapi_set_def_dependencies_recurse($field) {
355 $dependencies = array();
356
357 foreach (element_children($field) as $child) {
358 if ($field[$child]['#type'] != 'array' && isset($field[$child]['#reference_entity'])) {
359 $parent_entity = $field[$child]['#parents'][0];
360
361 // Any field that is a reference to another field in a different entity
362 // indicates the presence of a dependency.
363 if ($field[$child]['#reference_entity'] != $parent_entity) {
364 $dependencies[$field[$child]['#reference_entity']] = $field[$child]['#reference_entity'];
365 }
366 }
367
368 // Recurse down through entity fields, array fields, and nested arrays. The
369 // array union operator ensures that the list of dependencies is free of
370 // duplicate elements.
371 $dependencies += _importexportapi_set_def_dependencies_recurse($field[$child]);
372 }
373
374 return $dependencies;
375 }
376
377 /**
378 * Helper function for importexportapi_get_def(). Fetches the definition for
379 * the specified field, and optionally sets the attributes provided in $values
380 * before doing so.
381 *
382 * @param $field
383 * The field or entity for which to fetch a definition. This value should
384 * always be an array. If the field's direct parent is not an entity, then
385 * each element should be one of the field's ancestors (in order from
386 * furthest to closest ancestor). The final element is always the field
387 * itself.
388 * @param &$def
389 * The definition for the parent of the current field. This array, or a
390 * subset of it, gets returned; and if any changes are made to it, they get
391 * passed back by reference.
392 * @param $values
393 * The values to set for attributes of the specified field. If these values
394 * already exist, they will be overridden (for non-array values) or merged
395 * (for array values). Only set values for attributes here - do not modify
396 * child elements. Child elements should be modified by calling this function
397 * specifically for that child (defaults to NULL).
398 *
399 * @return
400 * The definition for the specified field, or NULL if the field could not be
401 * found.
402 */
403 function _importexportapi_set_def_values($field, &$def, $values = array()) {
404 // Grab the next parent off the array, or the current field if we're at the
405 // end.
406 $field_index = array_shift($field);
407
408 if (empty($field)) {
409 if (isset($def[$field_index])) {
410 // Overwrite the definition with any new values that have been provided.
411 if (!empty($values)) {
412 $def[$field_index] = _importexportapi_set_def_values_recurse($def[$field_index], $values);
413 }
414
415 return $def[$field_index];
416 }
417 // We have recursed through all parent fields, and the current field cannot
418 // be found - nothing more we can do.
419 return NULL;
420 }
421
422 // There are still parent fields available, so recurse down until we reach
423 // the current field.
424 return _importexportapi_set_def_values($field, $def[$field_index], $values);
425 }
426
427 /**
428 * Similar to array_merge_recursive but keyed-valued are always overwritten.
429 * Priority goes to the 2nd array. This function based on the
430 * array_merge_recursive2() function at:
431 * http://php.net/array-merge-recursive#42663
432 *
433 * @param $array1
434 * The first array to be recursively merged.
435 * @param $array2
436 * The second array to be recursively merged.
437 *
438 * @return
439 * The result of recursively merging the passed-in arrays.
440 */
441 function _importexportapi_set_def_values_recurse($array1, $array2) {
442 if (!is_array($array1) || !is_array($array2)) {
443 return $array2;
444 }
445
446 foreach ($array2 as $key2 => $value2) {
447 $array1_val = isset($array1[$key2]) ? $array1[$key2] : NULL;
448 $array1[$key2] = _importexportapi_set_def_values_recurse($array1_val, $value2);
449 }
450
451 return $array1;
452 }
453
454 /**
455 * Builds the data definitions. All of the 'build' callbacks for the specified
456 * 'get' and 'put' formats are executed, and alternate key fields are also
457 * copied and generated.
458 *
459 * @param $provided_formats
460 * A nested array in the form ('get' => array('format1', 'format2', ...),
461 * 'put' => array('format1', 'format2', ...)). All formats that are specified
462 * here will have their 'build' callbacks executed. You do not need to provide
463 * both 'get' and 'put'. You can also leave this field unspecified, in which
464 * case all callbacks for all available formats will be executed (default is
465 * NULL).
466 * @param $refresh
467 * Whether or not to reset the internal cache of this function (defaults to
468 * FALSE).
469 */
470 function importexportapi_build_def($provided_formats = NULL, $refresh = FALSE) {
471 static $is_built = FALSE;
472
473 // By default, the building process only happens once per request.
474 if ($is_built && !$refresh) {
475 return;
476 }
477
478 $def = importexportapi_get_def();
479 $formats = array();
480 $format_types = array('get', 'put');
481
482 // Populate the list of engines and associated formats for which to build the
483 // definitions. All formats are grouped into either 'get' or 'put', depending
484 // on their associated engine.
485 foreach ($format_types as $format_type) {
486 if (!isset($provided_formats[$format_type])) {
487 $formats[$format_type] = array();
488 $format_list = importexportapi_get_engines($format_type);
489 if (isset($format_list)) {
490 $formats[$format_type] = array_keys($format_list);
491 }
492 }
493 else {
494 if (!is_array($provided_formats[$format_type])) {
495 $provided_formats[$format_type] = array($provided_formats[$format_type]);
496 }
497
498 $formats[$format_type] = $provided_formats[$format_type];
499 }
500 }
501
502 // Run all 'build' callbacks that have been defined for the specified
503 // formats.
504 foreach ($formats as $format_type => $format_list) {
505 foreach ($format_list as $format) {
506 $callbacks = importexportapi_get_engines($format_type, $format);
507
508 if (isset($callbacks['build'])) {
509 foreach ($callbacks['build'] as $build => $args) {
510 if (function_exists($build)) {
511 foreach ($def as $entity => $fields) {
512 $args_build = array_merge(array($def[$entity]), $args);
513 call_user_func_array($build, $args_build);
514 // The 'build' callback makes its modifications to the definition
515 // through importexportapi_set_def(), so we have to call
516 // importexportapi_get_def() to get updated with these
517 // modifications.
518 $def[$entity] = importexportapi_get_def($entity);
519 }
520 }
521 }
522 }
523 }
524 }
525
526 // Alternate key generation occurs after the build callbacks have been
527 // executed, so that we are copying and generating fully built fields.
528 foreach ($def as $entity_id => $entity_data) {
529 importexportapi_set_alt_keys($entity_id, $entity_data, $formats);
530 }
531
532 $is_built = TRUE;
533 }
534
535 /**
536 * Retrieves the default properties or the SQL placeholder for the defined
537 * field type.
538 *
539 * @param $type
540 * The internal name of the field type. Leave NULL to retrieve info for all
541 * field types.
542 * @param $op
543 * The type of info to get. Leave NULL to retrieve default properties, or
544 * specify 'placeholders' to retrieve the SQL placeholder.
545 * @param $refresh
546 * Whether or not to reset the internal cache of this function (defaults to
547 * FALSE).
548 *
549 * @return
550 * The info for the specified type.
551 */
552 function importexportapi_get_field_type_info($type = NULL, $op = NULL, $refresh = FALSE) {
553 static $types;
554
555 if (!isset($op)) {
556 $op = 'defaults';
557 }
558 if (!isset($types)) {
559 $types = array();
560 }
561
562 if (!isset($types[$op]) || $refresh) {
563 $types[$op] = module_invoke_all('def_field_types', $op);
564 }
565
566 if (isset($type)) {
567 return isset($types[$op][$type]) ? $types[$op][$type] : array();
568 }
569 return $types[$op];
570 }
571
572 /**
573 * Retrieves the callback function of the engine for the specified type, that
574 * is associated with the specified format; or the callback functions of all
575 * engines of the specified type; or the callback functions all engines.
576 *
577 * @param $type
578 * The type of engine callback to retrieve. Can be either 'get', 'put', or
579 * NULL. Default is NULL.
580 * @param $format
581 * The format for which to retrieve an engine callbck (e.g. 'xml', 'csv').
582 * Default is NULL.
583 * @param $refresh
584 * Whether or not to reset the internal cache of this function (defaults to
585 * FALSE).
586 *
587 * @return
588 * The callback function of the relevant engine, as a string; or the callback
589 * functions of all engines of the specified type, as an array; or all
590 * callback functions of all engines, as an array.
591 */
592 function importexportapi_get_engines($type = NULL, $format = NULL, $refresh = FALSE) {
593 static $engines;
594
595 if (!isset($engines) || $refresh) {
596 $engines = array();
597 $hook_data = module_invoke_all('engines_get_put');
598
599 // Humans prefer to think of engines in terms of format, and then in terms
600 // of callback type. But the system prefers to think of engines in the
601 // opposite way. So the hooks define engine callbacks by format and then by
602 // callback type, and we then convert the hook data to be grouped by
603 // callback type and then by format.
604 foreach ($hook_data as $hook_format => $callbacks) {
605 foreach ($callbacks as $callback_type => $callback) {
606 if (!isset($engines[$callback_type]) && $callback_type != 'title') {
607 $engines[$callback_type] = array();
608 }
609
610 $engines[$callback_type][$hook_format] = $callback;
611 $engines[$callback_type][$hook_format]['title'] = $callbacks['title'];
612 }
613 }
614 }
615
616 if (isset($format)) {
617 return isset($engines[$type][$format]) ? $engines[$type][$format] : NULL;
618 }
619 if (isset($type)) {
620 return isset($engines[$type]) ? $engines[$type] : NULL;
621 }
622 return $engines;
623 }
624
625 /**
626 * Passes a set of entity definitions to the appropriate 'get' engine, and
627 * returns the data available for the specified entities, in a standard
628 * structured array format.
629 *
630 * @param $entities
631 * An array listing the entities to be gotten.
632 * @param $get_format
633 * The format of the data source for the specified entities. This
634 * determines the 'get' engine to be called upon. If set to NULL, then the
635 * format defined by the '#source' attribute of the first available entity is
636 * used instead. Default is NULL.
637 * @param $variables
638 * An array of variables (keys and values) to be passed to the 'get' engine.
639 * One common variable that gets passed is 'raw' (the raw data to be gotten).
640 * @param $put_formats
641 * An array of destination data formats, that the data definition should be
642 * built for. If you wish to perform a 'put' operation on the returned data
643 * at a later stage, you will only be able to perform it using one of the
644 * formats that has been specified here. A single format may be entered as a
645 * string instead of an array. If set to NULL, then the data gets built for
646 * all available 'put' formats. Default is NULL.
647 * @param $def
648 * The entity definitions to use. If none are provided, then the standard
649 * definitions will be gotten from importexportapi_get_def() (default is
650 * NULL).
651 *
652 * @return
653 * Structured array of data, suitable for being passed to
654 * importexportapi_put_data().
655 */
656 function importexportapi_get_data($entities, $get_format = NULL, $variables = array(), $def = NULL) {
657 if (!isset($def)) {
658 $def = importexportapi_get_def(NULL, TRUE);
659 }
660
661 // If no 'get' format has been specified, then use the 'source' format of the
662 // first available entity (default 'source' format is 'db').
663 if (!isset($get_format)) {
664 $get_format = $def[$entities[0]]['#source'];
665 }
666
667 $get_callbacks = importexportapi_get_engines('get', $get_format);
668 if (!isset($get_callbacks['callback'])) {
669 return NULL;
670 }
671
672 $function = $get_callbacks['callback'];
673 if (!function_exists($function)) {
674 return NULL;
675 }
676
677 // Run the 'get' operations.
678 $data = array();
679 $entities = array_flip($entities);
680 foreach (array_keys($def) as $entity) {
681 if (isset($entities[$entity])) {
682 $data = array_merge($data, $function($def[$entity], $variables));
683 }
684 }
685
686 // Execute 'process' 'get' callbacks.
687 importexportapi_process_data($data, $get_format, 'get');
688
689 $data['#source_get'] = $get_format;
690
691 return $data;
692 }
693
694 /**
695 * Passes a set of structured data to the appropriate 'put' engine, and
696 * returns the result of the 'put' operation.
697 *
698 * @param $data
699 * An array of structured data, as returned by importexportapi_get_data().
700 * @param $put_format
701 * The format of the data destination for the structured data. This
702 * determines the 'put' engine to be called upon. If set to NULL, then the
703 * format defined by the '#source' attribute of the first available entity
704 * instance (in the structured data provided) is used instead. Default is
705 * NULL.
706 * @param $variables
707 * An array of variables (keys and values) to be passed to the 'put' engine.
708 *
709 * @return
710 * The result of the 'put' operation. This varies from one engine to another.
711 * Some engines (such as 'db') only return status codes, whereas others (such
712 * as 'xml') return the data itself in its final rendered form.
713 */
714 function importexportapi_put_data($data, $put_format = NULL, $variables = array()) {
715 if (!isset($put_format)) {
716 $put_format = $data[0]['#source'];
717 }
718
719 $put_callbacks = importexportapi_get_engines('put', $put_format);
720 $function = $put_callbacks['callback'];
721
722 if (!isset($function) || !function_exists($function)) {
723 return NULL;
724 }
725
726 importexportapi_process_data($data, $put_format, 'put');
727
728 return $function($data, $variables);
729 }
730
731 /**
732 * Searches for fields that reference other fields, and checks to see if those
733 * other fields have alternate key fields. Every alternate key field that is
734 * found gets copied alongside the referencing field. Essentially, new fields
735 * get generated, and those fields are copies of defined alternate key fields.
736 * All generated fields are explicitly marked with the '#generated' attribute,
737 * to make it clear to other parts of the system that they are not
738 * user-defined.
739 *
740 * @param $field_id
741 * The ID of the field specifed.
742 * @param $field
743 * The field that is a reference to another field, that in turn may have
744 * alternate key fields defined for it. All alternate key fields that are
745 * found will be copied (generated) alongside this field, using the
746 * importexportapi_set_def() function (thus nothing is returned).
747 * @param $formats
748 * An array of 'get' and 'put' formats and their callbacks.
749 */
750 function importexportapi_set_alt_keys($field_id, $field, $formats) {
751 $alt_key_ignore = isset($field['#alt_key_ignore']) ? $field['#alt_key_ignore'] : array();
752 $alt_key_ignore_all = is_bool($alt_key_ignore) ? $alt_key_ignore : FALSE;
753
754 // We only care about this field if it references another field.
755 if (isset($field['#reference_field']) && !$alt_key_ignore_all) {
756 $reference_index = array_pop($field['#reference_field']);
757 $ref_parent_array = array_merge(array($field['#reference_entity']), $field['#reference_field']);
758
759 // Ignore this field if it's a sibling of the field that it's referencing.
760 $parent_diff = array_diff($field['#parents'], $ref_parent_array);
761 $parent_intersect = array_intersect($field['#parents'], $ref_parent_array);
762 if (!empty($parent_diff) || (!empty($parent_intersect) && count($field['#parents']) <= count($ref_parent_array))) {
763 // Grab the definition for the parent of the field being referenced.
764 $ref_parent = importexportapi_get_def($ref_parent_array);
765
766 if (!empty($ref_parent)) {
767 // Every child of $ref_parent is a 'candidate' for being copied over. Only
768 // the lucky children actually get copied, though. :)
769 foreach (element_children($ref_parent) as $candidate_id) {
770 $alt_key_candidate = $ref_parent[$candidate_id];
771 $existing_candidate = importexportapi_get_def(array_merge($field['#parents'], array($alt_key_candidate['#id'])));
772 if (isset($alt_key_candidate['#alt_key_for']) && !isset($alt_key_candidate['#generated']) && $alt_key_candidate['#alt_key_for'] == $reference_index && !isset($alt_key_ignore[$alt_key_candidate['#id']])) {
773 $values = array();
774 $alt_key_rename = isset($existing_candidate);
775
776 // Rename the field's ID if it is not unique.
777 if ($alt_key_rename) {
778 $alt_key_candidate['#id'] = $field_id .'_'. $alt_key_candidate['#id'];
779 $alt_key_candidate['#alt_key_rename'] = $field_id;
780 }
781 $existing_candidate = importexportapi_get_def(array_merge($field['#parents'], array($alt_key_candidate['#id'])));
782
783 if (!isset($existing_candidate)) {
784 // Change some attributes of the copied field, to reflect its new
785 // context.
786 $alt_key_candidate['#reference_entity'] = $alt_key_candidate['#parents'][0];
787 $alt_key_candidate['#reference_field'] = $alt_key_candidate['#parents'];
788 array_shift($alt_key_candidate['#reference_field']);
789 $alt_key_candidate['#reference_field'][] = $candidate_id;
790 $alt_key_candidate['#reference_delta'] = $field['#reference_delta'];
791 $alt_key_candidate['#reference_parent'] = isset($field['#reference_parent']) ? $field['#reference_parent'] : NULL;
792 $alt_key_candidate['#parents'] = $field['#parents'];
793 $alt_key_candidate['#key_component'] = FALSE;
794 $alt_key_candidate['#alt_key_for'] = $field_id;
795 // Explicitly mark this field as 'generated'.
796 $alt_key_candidate['#generated'] = TRUE;
797
798 // Save the new copy of the field to the master definition store.
799 $values[$alt_key_candidate['#id']] = $alt_key_candidate;
800 importexportapi_set_def($field['#parents'], $values);
801
802 // Allow further building to occur for the new generated field.
803 importexportapi_build_alt_key($alt_key_candidate, $formats);
804 }
805 }
806 }
807 }
808 }
809 }
810
811 // Recurse down through all children of this field.
812 foreach (element_children($field) as $child) {
813 importexportapi_set_alt_keys($child, $field[$child], $formats);
814 }
815 }
816
817 /**
818 * Builds alternate keys after they have been generated.
819 *
820 * @param $alt_key
821 * The definition for the generated alternate key.
822 * @param $formats
823 * An array of 'get' and 'put' formats and their callbacks.
824 */
825 function importexportapi_build_alt_key($alt_key, $formats) {
826 // Run all 'build_alt_key' callbacks that have been defined for the specified
827 // formats.
828 foreach ($formats as $format_type => $format_list) {
829 foreach ($format_list as $format) {
830 $callbacks = importexportapi_get_engines($format_type, $format);
831
832 if (isset($callbacks['build_alt_key'])) {
833 foreach ($callbacks['build_alt_key'] as $build => $args) {
834 if (function_exists($build)) {
835 $args_build = array_merge(array($alt_key), $args);
836 call_user_func_array($build, $args_build);
837 }
838 }
839 }
840 }
841 }
842 }
843
844 /**
845 * Executes the '#process' callback on a set of data. The processed data is
846 * passed back by reference.
847 *
848 * @param &$data
849 * The set of data to be processed, as returned by a 'get' operation.
850 * @param $format
851 * The format of the data source (for 'get' processing) or of the data
852 * destination (for 'put' processing).
853 * @param $op
854 * The operation that the data is being processed for (either 'get' or
855 * 'put').
856 */
857 function importexportapi_process_data(&$data, $format, $op) {
858 $callbacks = importexportapi_get_engines($op, $format);
859 if (isset($callbacks['process'])) {
860 foreach ($callbacks['process'] as $process => $args) {
861 if (function_exists($process)) {
862 // Call the 'process' callback for this engine, is one is defined.
863 $args = array_merge(array($data), $args);
864 $data = call_user_func_array($process, $args);
865 }
866 }
867 }
868
869 $source_get = isset($data['#source_get']) ? $data['#source_get'] : NULL;
870
871 foreach (element_children($data) as $data_index) {
872 if (is_numeric($data_index)) {
873 if (isset($data[$data_index]['#process']) && empty($data[$data_index]["#processed_$op"])) {
874 foreach ($data[$data_index]['#process'] as $process => $args) {
875 if (function_exists($process)) {
876 // Call the 'process' callback for this field, is one is defined.
877 $args = array_merge(array($data[$data_index]['#id'], $data[$data_index], $format, $op, $source_get), $args);
878 $data[$data_index] = call_user_func_array($process, $args);
879 }
880 }
881 $data[$data_index]["#processed_$op"] = TRUE;
882 }
883 foreach (element_children($data[$data_index]) as $field) {
884 // Allow for (recursive) custom processing of different field types.
885 _importexportapi_process_data($field, $data[$data_index][$field], $format, $op, $source_get);
886 }
887 }
888 }
889
890 if ($op == 'put') {
891 unset($data['#source_get']);
892 }
893 }
894
895 /**
896 * Helper function for importexportapi_process_data(). Executes the 'process'
897 * callback recursively, for $data and all its children.
898 *
899 * @param $parent_field
900 * The field ID of the parent field of $data.
901 * @param &$data
902 * The field for which to execute the 'process' callback. Any values within
903 * this variable may be changed and passed back by reference.
904 */
905 function _importexportapi_process_data($parent_field, &$data, $format, $op, $source_get) {
906 if (isset($data['#process']) && empty($data["#processed_$op"])) {
907 foreach ($data['#process'] as $process => $args) {
908 if (function_exists($process)) {
909 // Call the 'process' callback for this field, is one is defined.
910 $args = array_merge(array($parent_field, $data, $format, $op, $source_get), $args);
911 $data = call_user_func_array($process, $args);
912 }
913 }
914 $data["#processed_$op"] = TRUE;
915 }
916
917 if ($data['#type'] == 'array' && isset($data['#value'])) {
918 foreach ($data['#value'] as $data_index => $data_fields) {
919 foreach (element_children($data['#value'][$data_index]) as $field) {
920 // Recurse down through all child fields.
921 _importexportapi_process_data($field, $data['#value'][$data_index][$field], $format, $op, $source_get);
922 }
923 }
924 }
925 }
926
927 /**
928 * Typecasts a value to its correct primitive type, after it has been
929 * imported from a raw string.
930 *
931 * @param $type
932 * The type of the field, as defined in its data definition (this may be
933 * different to its type as defined by PHP).
934 * @param $value
935 * The value of the field, as gotten from a raw string.
936 */
937 function importexportapi_string_typecast($type, &$value) {
938 if (isset($value)) {
939 switch ($type) {
940 case 'int':
941 $value = (int) $value;
942 break;
943 case 'float':
944 $value = (float) $value;
945 break;
946 default:
947 break;
948 }
949 }
950 }
951
952 /**
953 * Loads the data definition files that are included with the core module.
954 */
955 function _importexportapi_include_core_defs() {
956 // Acknowledgement: below code based on similar code in the views module.
957 // Load all our module 'on behalfs'.
958 $path = drupal_get_path('module', 'importexportapi') .'/definitions';
959 $files = drupal_system_listing('importexportapi_.*\.inc$', $path, 'name', 0);
960
961 foreach ($files as $file) {
962 // The filename format is very specific. It must be
963 // importexportapi_MODULENAME.inc
964 $module = substr_replace($file->name, '', 0, 16);
965 if (module_exists($module)) {
966 require_once('./'. $file->filename);
967 }
968 }
969 }
970
971 /**
972 * Loads the 'get' and 'put' engine files that are included with the core
973 * module.
974 */
975 function _importexportapi_include_core_engines() {
976 $path = drupal_get_path('module', 'importexportapi') .'/engines';
977 $files = drupal_system_listing('importexportapi_.*\.inc$', $path, 'name', 0);
978 $engines = importexportapi_engines_get_put();
979 $engine_names = array();
980
981 foreach ($engines as $engine => $callbacks) {
982 foreach ($callbacks as $callback => $callback_functions) {
983 $engine_names['importexportapi_'. $engine .'_'. $callback] = TRUE;
984 }
985 }
986
987 foreach ($files as $file) {
988 // The filename format is very specific. It must be
989 // importexportapi_ENGINE_CALLBACK.inc
990 if (isset($engine_names[$file->name])) {
991 require_once('./'. $file->filename);
992 }
993 }
994 }
995
996 /**
997 * Loads the field callback files that are included with the core module.
998 */
999 function _importexportapi_include_core_fields() {
1000 // Acknowledgement: below code based on similar code in the views module.
1001 // Load all our module 'on behalfs'.
1002 $path = drupal_get_path('module', 'importexportapi') .'/fields';
1003 $files = drupal_system_listing('importexportapi_.*\.inc$', $path, 'name', 0);
1004 $fields = importexportapi_get_field_type_info();
1005
1006 foreach ($files as $file) {
1007 // The filename format is very specific. It must be
1008 // importexportapi_MODULENAME.inc
1009 $field = substr_replace($file->name, '', 0, 16);
1010 if (isset($fields[$field])) {
1011 require_once('./'. $file->filename);
1012 }
1013 }
1014 }

  ViewVC Help
Powered by ViewVC 1.1.2