/** * @param array $pa_options * ignoreContext = don't switch context even though context may be set for current item */ public function processExporterItem($pn_item_id, $pn_table_num, $pn_record_id, $pa_options = array()) { $vb_ignore_context = isset($pa_options['ignoreContext']) && $pa_options['ignoreContext']; $t_exporter_item = ca_data_exporters::loadExporterItemByID($pn_item_id); $t_instance = ca_data_exporters::loadInstanceByID($pn_record_id, $pn_table_num); // switch context to a different set of records if necessary and repeat current exporter item for all those selected records // (e.g. hierarchy children or related items in another table, restricted by types or relationship types) if (!$vb_ignore_context && ($vs_context = $t_exporter_item->getSetting('context'))) { $va_restrict_to_types = $t_exporter_item->getSetting('restrictToTypes'); $va_restrict_to_rel_types = $t_exporter_item->getSetting('restrictToRelationshipTypes'); $va_check_access = $t_exporter_item->getSetting('checkAccess'); $vn_new_table_num = $this->getAppDatamodel()->getTableNum($vs_context); if ($vn_new_table_num) { // switch to new table $vs_key = $this->getAppDatamodel()->getTablePrimaryKeyName($vs_context); } else { // this table, i.e. hierarchy context switch $vs_key = $t_instance->primaryKey(); $vn_new_table_num = $pn_table_num; } switch ($vs_context) { case 'children': $va_related = $t_instance->getHierarchyChildren(); break; case 'parent': $va_related = array(); if ($vs_parent_id_fld = $t_instance->getProperty("HIERARCHY_PARENT_ID_FLD")) { $va_related[] = array($vs_key => $t_instance->get($vs_parent_id_fld)); } break; case 'ancestors': $va_parents = $t_instance->getHierarchyAncestors(null, array('idsOnly' => true)); $va_related = array(); foreach (array_unique($va_parents) as $vn_pk) { $va_related[] = array($vs_key => intval($vn_pk)); } break; case 'ca_sets': $t_set = new ca_sets(); $va_set_options = array(); if (isset($va_restrict_to_types[0])) { // the utility used below doesn't support passing multiple types so we just pass the first. // this should be enough for 99.99% of the actual use cases anyway $va_set_options['setType'] = $va_restrict_to_types[0]; } $va_set_options['checkAccess'] = $va_check_access; $va_set_options['setIDsOnly'] = true; $va_set_ids = $t_set->getSetsForItem($pn_table_num, $t_instance->getPrimaryKey(), $va_set_options); $va_related = array(); foreach (array_unique($va_set_ids) as $vn_pk) { $va_related[] = array($vs_key => intval($vn_pk)); } break; default: // plain old related table if ($vn_new_table_num) { $va_related = $t_instance->getRelatedItems($vs_context, array('restrictToTypes' => $va_restrict_to_types, 'restrictToRelationshipTypes' => $va_restrict_to_rel_types, 'checkAccess' => $va_check_access)); } else { return array(); } break; } $va_info = array(); if (is_array($va_related)) { foreach ($va_related as $va_rel) { $va_rel_export = $this->processExporterItem($pn_item_id, $vn_new_table_num, $va_rel[$vs_key], array_merge(array('ignoreContext' => true), $pa_options)); $va_info = array_merge($va_info, $va_rel_export); } } return $va_info; } // end switch context // Don't prevent context switches for children of context-switched exporter items. This way you can // build cascades for jobs like exporting objects related to the creator of the record in question. unset($pa_options['ignoreContext']); $va_item_info = array(); $vs_source = $t_exporter_item->get('source'); $vs_element = $t_exporter_item->get('element'); $vb_repeat = $t_exporter_item->getSetting('repeat_element_for_multiple_values'); // if omitIfEmpty is set and returns nothing, we ignore this exporter item and all children if ($vs_omit_if_empty = $t_exporter_item->getSetting('omitIfEmpty')) { if (!(strlen($t_instance->get($vs_omit_if_empty)) > 0)) { return array(); } } // always return URL for export, not an HTML tag $va_get_options = array('returnURL' => true); if ($t_exporter_item->getSetting('convertCodesToDisplayText')) { $va_get_options['convertCodesToDisplayText'] = true; } if ($vs_delimiter = $t_exporter_item->getSetting("delimiter")) { $va_get_options['delimiter'] = $vs_delimiter; } if ($vs_template = $t_exporter_item->getSetting('template')) { $va_get_options['template'] = $vs_template; } if ($vs_locale = $t_exporter_item->getSetting('locale')) { // the global UI locale for some reason has a higher priority // than the locale setting in BaseModelWithAttributes::get // which is why we unset it here and restore it later global $g_ui_locale; $vs_old_ui_locale = $g_ui_locale; $g_ui_locale = null; $va_get_options['locale'] = $vs_locale; } if ($vs_source) { $va_matches = array(); // CONSTANT value if (preg_match("/^_CONSTANT_:(.*)\$/", $vs_source, $va_matches)) { $va_item_info[] = array('text' => trim($va_matches[1]), 'element' => $vs_element); } else { if (!$vb_repeat) { $va_item_info[] = array('text' => $t_instance->get($vs_source, $va_get_options), 'element' => $vs_element); } else { // if user wants current element repeated in case of multiple returned values, go ahead and do that $va_get_options['delimiter'] = ';#;'; $vs_values = $t_instance->get($vs_source, $va_get_options); $va_tmp = explode(";#;", $vs_values); foreach ($va_tmp as $vs_text) { $va_item_info[] = array('element' => $vs_element, 'text' => $vs_text); } } } } else { if ($vs_template) { // templates without source are probably just static text, but you never know // -> run them through processor anyways $va_item_info[] = array('element' => $vs_element, 'text' => caProcessTemplateForIDs($vs_template, $pn_table_num, array($pn_record_id), array())); } else { // no source, no template -> probably wrapper $va_item_info[] = array('element' => $vs_element); } } // reset UI locale if we unset it if ($vs_locale) { $g_ui_locale = $vs_old_ui_locale; } // handle settings and plugin hooks $vs_default = $t_exporter_item->getSetting('default'); $vs_prefix = $t_exporter_item->getSetting('prefix'); $vs_suffix = $t_exporter_item->getSetting('suffix'); $vs_regexp = $t_exporter_item->getSetting('filterByRegExp'); $vn_maxlength = $t_exporter_item->getSetting('maxLength'); $vs_original_values = $t_exporter_item->getSetting('original_values'); $vs_replacement_values = $t_exporter_item->getSetting('replacement_values'); $va_replacements = ca_data_exporter_items::getReplacementArray($vs_original_values, $vs_replacement_values); foreach ($va_item_info as $vn_key => &$va_item) { // if returned value is null then we skip the item if (is_null($va_plugin_item = $this->opo_app_plugin_manager->hookExportItemBeforeSettings(array('instance' => $t_instance, 'exporter_item_instance' => $t_exporter_item, 'export_item' => $va_item)))) { continue; } else { $va_item = $va_plugin_item['export_item']; } // filter by regexp if (strlen($va_item['text']) > 0 && $vs_regexp) { if (!preg_match("!" . $vs_regexp . "!", $va_item['text'])) { unset($va_item_info[$vn_key]); continue; } } // do replacements $va_item['text'] = ca_data_exporter_items::replaceText($va_item['text'], $va_replacements); // if text is empty, fill in default // if text isn't empty, respect prefix and suffix if (strlen($va_item['text']) == 0) { if ($vs_default) { $va_item['text'] = $vs_default; } } else { if (strlen($vs_prefix) > 0 || strlen($vs_suffix) > 0) { $va_item['text'] = $vs_prefix . $va_item['text'] . $vs_suffix; } } if ($vn_maxlength && strlen($va_item['text']) > $vn_maxlength) { $va_item['text'] = substr($va_item['text'], 0, $vn_maxlength) . " ..."; } // if returned value is null then we skip the item if (is_null($va_plugin_item = $this->opo_app_plugin_manager->hookExportItem(array('instance' => $t_instance, 'exporter_item_instance' => $t_exporter_item, 'export_item' => $va_item)))) { continue; } else { $va_item = $va_plugin_item['export_item']; } } foreach ($t_exporter_item->getHierarchyChildren() as $va_child) { foreach ($va_item_info as &$va_item) { $va_child_export = $this->processExporterItem($va_child['item_id'], $pn_table_num, $pn_record_id, $pa_options); $va_item['children'] = array_merge((array) $va_item['children'], $va_child_export); } } return $va_item_info; }
/** * Processes single exporter item for a given record * * @param int $pn_item_id Primary of exporter item * @param int $pn_table_num Table num of item to export * @param int $pn_record_id Primary key value of item to export * @param array $pa_options * ignoreContext = don't switch context even though context may be set for current item * relationship_type_id, relationship_type_code, relationship_typename = * if this export is a sub-export (context-switch), we have no way of knowing the relationship * to the 'parent' element in the export, so there has to be a means to pass it down to make it accessible * attribute_id = signals that this is an export relative to a specific attribute instance * this triggers special behavior that allows getting container values in a kind of sub-export * it's really only useful for Containers but in theory can be any attribute * logger = KLogger instance to use for logging. This option is mandatory! * @return array Item info */ public function processExporterItem($pn_item_id, $pn_table_num, $pn_record_id, $pa_options = array()) { $o_log = caGetOption('logger', $pa_options); // always set by exportRecord() $vb_ignore_context = caGetOption('ignoreContext', $pa_options); $vn_attribute_id = caGetOption('attribute_id', $pa_options); $o_log->logInfo(_t("Export mapping processor called with parameters [exporter_item_id:%1 table_num:%2 record_id:%3]", $pn_item_id, $pn_table_num, $pn_record_id)); $t_exporter_item = ca_data_exporters::loadExporterItemByID($pn_item_id); $t_instance = ca_data_exporters::loadInstanceByID($pn_record_id, $pn_table_num); // switch context to a different set of records if necessary and repeat current exporter item for all those selected records // (e.g. hierarchy children or related items in another table, restricted by types or relationship types) if (!$vb_ignore_context && ($vs_context = $t_exporter_item->getSetting('context'))) { $va_restrict_to_types = $t_exporter_item->getSetting('restrictToTypes'); $va_restrict_to_rel_types = $t_exporter_item->getSetting('restrictToRelationshipTypes'); $va_restrict_to_bundle_vals = $t_exporter_item->getSetting('restrictToBundleValues'); $va_check_access = $t_exporter_item->getSetting('checkAccess'); $va_sort = $t_exporter_item->getSetting('sort'); $vn_new_table_num = $this->getAppDatamodel()->getTableNum($vs_context); $vb_context_is_related_table = false; $va_related = null; if ($vn_new_table_num) { // switch to new table $vs_key = $this->getAppDatamodel()->getTablePrimaryKeyName($vs_context); } else { // this table, i.e. hierarchy context switch $vs_key = $t_instance->primaryKey(); } $o_log->logInfo(_t("Initiating context switch to '%1' for mapping ID %2 and record ID %3. The processor now tries to find matching records for the switch and calls itself for each of those items.", $vs_context, $pn_item_id, $pn_record_id)); switch ($vs_context) { case 'children': $va_related = $t_instance->getHierarchyChildren(); break; case 'parent': $va_related = array(); if ($vs_parent_id_fld = $t_instance->getProperty("HIERARCHY_PARENT_ID_FLD")) { $va_related[] = array($vs_key => $t_instance->get($vs_parent_id_fld)); } break; case 'ancestors': $va_parents = $t_instance->getHierarchyAncestors(null, array('idsOnly' => true)); $va_related = array(); foreach (array_unique($va_parents) as $vn_pk) { $va_related[] = array($vs_key => intval($vn_pk)); } break; case 'ca_sets': $t_set = new ca_sets(); $va_set_options = array(); if (isset($va_restrict_to_types[0])) { // the utility used below doesn't support passing multiple types so we just pass the first. // this should be enough for 99.99% of the actual use cases anyway $va_set_options['setType'] = $va_restrict_to_types[0]; } $va_set_options['checkAccess'] = $va_check_access; $va_set_options['setIDsOnly'] = true; $va_set_ids = $t_set->getSetsForItem($pn_table_num, $t_instance->getPrimaryKey(), $va_set_options); $va_related = array(); foreach (array_unique($va_set_ids) as $vn_pk) { $va_related[] = array($vs_key => intval($vn_pk)); } break; case 'ca_list_items.firstLevel': if ($t_instance->tableName() == 'ca_lists') { $o_dm = Datamodel::load(); $va_related = array(); $va_items_legacy_format = $t_instance->getListItemsAsHierarchy(null, array('maxLevels' => 1, 'dontIncludeRoot' => true)); $vn_new_table_num = $o_dm->getTableNum('ca_list_items'); $vs_key = 'item_id'; foreach ($va_items_legacy_format as $va_item_legacy_format) { $va_related[$va_item_legacy_format['NODE']['item_id']] = $va_item_legacy_format['NODE']; } break; } else { return array(); } break; default: if ($vn_new_table_num) { $va_options = array('restrictToTypes' => $va_restrict_to_types, 'restrictToRelationshipTypes' => $va_restrict_to_rel_types, 'restrictToBundleValues' => $va_restrict_to_bundle_vals, 'checkAccess' => $va_check_access, 'sort' => $va_sort); $o_log->logDebug(_t("Calling getRelatedItems with options: %1.", print_r($va_options, true))); $va_related = $t_instance->getRelatedItems($vs_context, $va_options); $vb_context_is_related_table = true; } else { // container or invalid context $va_context_tmp = explode('.', $vs_context); if (sizeof($va_context_tmp) != 2) { $o_log->logError(_t("Invalid context %1. Ignoring this mapping.", $vs_context)); return array(); } $va_attrs = $t_instance->getAttributesByElement($va_context_tmp[1]); $va_info = array(); if (is_array($va_attrs) && sizeof($va_attrs) > 0) { $o_log->logInfo(_t("Switching context for element code: %1.", $va_context_tmp[1])); $o_log->logDebug(_t("Raw attribute value array is as follows. The mapping will now be repeated for each (outer) attribute. %1", print_r($va_attrs, true))); foreach ($va_attrs as $vo_attr) { $va_attribute_export = $this->processExporterItem($pn_item_id, $pn_table_num, $pn_record_id, array_merge(array('ignoreContext' => true, 'attribute_id' => $vo_attr->getAttributeID()), $pa_options)); $va_info = array_merge($va_info, $va_attribute_export); } } else { $o_log->logInfo(_t("Switching context for element code %1 failed. Either there is no attribute with that code attached to the current row or the code is invalid. Mapping is ignored for current row.", $va_context_tmp[1])); } return $va_info; } break; } $va_info = array(); if (is_array($va_related)) { $o_log->logDebug(_t("The current mapping will now be repreated for these items: %1", print_r($va_related, true))); if (!$vn_new_table_num) { $vn_new_table_num = $pn_table_num; } foreach ($va_related as $va_rel) { // if we're dealing with a related table, pass on some info the relationship type to the context-switched invocation of processExporterItem(), // because we can't access that information from the related item simply because we don't exactly know where the call originated if ($vb_context_is_related_table) { $pa_options['relationship_typename'] = $va_rel['relationship_typename']; $pa_options['relationship_type_code'] = $va_rel['relationship_type_code']; $pa_options['relationship_type_id'] = $va_rel['relationship_type_id']; } $va_rel_export = $this->processExporterItem($pn_item_id, $vn_new_table_num, $va_rel[$vs_key], array_merge(array('ignoreContext' => true), $pa_options)); $va_info = array_merge($va_info, $va_rel_export); } } else { $o_log->logDebug(_t("No matching related items found for last context switch")); } return $va_info; } // end switch context // Don't prevent context switches for children of context-switched exporter items. This way you can // build cascades for jobs like exporting objects related to the creator of the record in question. unset($pa_options['ignoreContext']); $va_item_info = array(); $vs_source = $t_exporter_item->get('source'); $vs_element = $t_exporter_item->get('element'); $vb_repeat = $t_exporter_item->getSetting('repeat_element_for_multiple_values'); // if omitIfEmpty is set and get() returns nothing, we ignore this exporter item and all children if ($vs_omit_if_empty = $t_exporter_item->getSetting('omitIfEmpty')) { if (!(strlen($t_instance->get($vs_omit_if_empty)) > 0)) { return array(); } } // if omitIfNotEmpty is set and get() returns a value, we ignore this exporter item and all children if ($vs_omit_if_not_empty = $t_exporter_item->getSetting('omitIfNotEmpty')) { if (strlen($t_instance->get($vs_omit_if_not_empty)) > 0) { return array(); } } // always return URL for export, not an HTML tag $va_get_options = array('returnURL' => true); if ($vs_delimiter = $t_exporter_item->getSetting("delimiter")) { $va_get_options['delimiter'] = $vs_delimiter; } if ($vs_template = $t_exporter_item->getSetting('template')) { $va_get_options['template'] = $vs_template; } if ($vs_locale = $t_exporter_item->getSetting('locale')) { // the global UI locale for some reason has a higher priority // than the locale setting in BaseModelWithAttributes::get // which is why we unset it here and restore it later global $g_ui_locale; $vs_old_ui_locale = $g_ui_locale; $g_ui_locale = null; $va_get_options['locale'] = $vs_locale; } // AttributeValue settings that are simply passed through by the exporter if ($t_exporter_item->getSetting('convertCodesToDisplayText')) { $va_get_options['convertCodesToDisplayText'] = true; // try to return text suitable for display for system lists stored in intrinsics (ex. ca_objects.access, ca_objects.status, ca_objects.source_id) // this does not affect list attributes } else { $va_get_options['convertCodesToIdno'] = true; // if display text is not requested try to return list item idno's... since underlying integer ca_list_items.item_id values are unlikely to be useful in an export context } if ($t_exporter_item->getSetting('returnIdno')) { $va_get_options['returnIdno'] = true; } if ($t_exporter_item->getSetting('start_as_iso8601')) { $va_get_options['start_as_iso8601'] = true; } if ($t_exporter_item->getSetting('end_as_iso8601')) { $va_get_options['end_as_iso8601'] = true; } if ($t_exporter_item->getSetting('dontReturnValueIfOnSameDayAsStart')) { $va_get_options['dontReturnValueIfOnSameDayAsStart'] = true; } if ($vs_date_format = $t_exporter_item->getSetting('dateFormat')) { $va_get_options['dateFormat'] = $vs_date_format; } // context was switched to attribute if ($vn_attribute_id) { $o_log->logInfo(_t("Processing mapping in attribute mode for attribute_id = %1.", $vn_attribute_id)); if ($vs_source) { // trying to find the source only makes sense if the source is set $t_attr = new ca_attributes($vn_attribute_id); $va_values = $t_attr->getAttributeValues(); $va_src_tmp = explode('.', $vs_source); if (sizeof($va_src_tmp) == 2) { $o_dm = Datamodel::load(); if ($t_attr->get('table_num') == $o_dm->getTableNum($va_src_tmp[0])) { $vs_source = $va_src_tmp[1]; } } $o_log->logDebug(_t("Trying to find code %1 in value array for the current attribute.", $vs_source)); $o_log->logDebug(_t("Value array is %1.", print_r($va_values, true))); foreach ($va_values as $vo_val) { $va_display_val_options = array(); if ($vo_val instanceof ListAttributeValue) { // figure out list_id -- without it we can't pull display values $t_element = new ca_metadata_elements($vo_val->getElementID()); $va_display_val_options = array('list_id' => $t_element->get('list_id')); if ($t_exporter_item->getSetting('returnIdno') || $t_exporter_item->getSetting('convertCodesToIdno')) { $va_display_val_options['output'] = 'idno'; } elseif ($t_exporter_item->getSetting('convertCodesToDisplayText')) { $va_display_val_options['output'] = 'text'; } } $o_log->logDebug(_t("Trying to match code from array %1 and the code we're looking for %2.", $vo_val->getElementCode(), $vs_source)); if ($vo_val->getElementCode() == $vs_source) { $vs_display_value = $vo_val->getDisplayValue($va_display_val_options); $o_log->logDebug(_t("Found value %1.", $vs_display_value)); $va_item_info[] = array('text' => $vs_display_value, 'element' => $vs_element); } } } else { // no source in attribute context probably means this is some form of wrapper, e.g. a MARC field $va_item_info[] = array('element' => $vs_element); } } else { if ($vs_source) { $o_log->logDebug(_t("Source for current mapping is %1", $vs_source)); $va_matches = array(); // CONSTANT value if (preg_match("/^_CONSTANT_:(.*)\$/", $vs_source, $va_matches)) { $o_log->logDebug(_t("This is a constant. Value for this mapping is '%1'", trim($va_matches[1]))); $va_item_info[] = array('text' => trim($va_matches[1]), 'element' => $vs_element); } else { if (in_array($vs_source, array("relationship_type_id", "relationship_type_code", "relationship_typename"))) { if (isset($pa_options[$vs_source]) && strlen($pa_options[$vs_source]) > 0) { $o_log->logDebug(_t("Source refers to releationship type information. Value for this mapping is '%1'", $pa_options[$vs_source])); $va_item_info[] = array('text' => $pa_options[$vs_source], 'element' => $vs_element); } } else { if (!$vb_repeat) { $vs_get = $t_instance->get($vs_source, $va_get_options); $o_log->logDebug(_t("Source is a simple get() for some bundle. Value for this mapping is '%1'", $vs_get)); $o_log->logDebug(_t("get() options are: %1", print_r($va_get_options, true))); $va_item_info[] = array('text' => $vs_get, 'element' => $vs_element); } else { // user wants current element repeated in case of multiple returned values $va_get_options['delimiter'] = ';#;'; $vs_values = $t_instance->get($vs_source, $va_get_options); $o_log->logDebug(_t("Source is a get() that should be repeated for multiple values. Value for this mapping is '%1'. It includes the custom delimiter ';#;' that is later used to split the value into multiple values.", $vs_values)); $o_log->logDebug(_t("get() options are: %1", print_r($va_get_options, true))); $va_tmp = explode(";#;", $vs_values); foreach ($va_tmp as $vs_text) { $va_item_info[] = array('element' => $vs_element, 'text' => $vs_text); } } } } } else { if ($vs_template) { // templates without source are probably just static text, but you never know // -> run them through processor anyways $vs_proc_template = caProcessTemplateForIDs($vs_template, $pn_table_num, array($pn_record_id), array()); $o_log->logDebug(_t("Current mapping has no source but a template '%1'. Value from extracted via template processor is '%2'", $vs_template, $vs_proc_template)); $va_item_info[] = array('element' => $vs_element, 'text' => $vs_proc_template); } else { // no source, no template -> probably wrapper $o_log->logDebug(_t("Current mapping has no source and no template and is probably an XML/MARC wrapper element")); $va_item_info[] = array('element' => $vs_element); } } } // reset UI locale if we unset it if ($vs_locale) { $g_ui_locale = $vs_old_ui_locale; } $o_log->logDebug(_t("We're now processing other settings like default, prefix, suffix, skipIfExpression, filterByRegExp, maxLength, plugins and replacements for this mapping")); $o_log->logDebug(_t("Local data before processing is: %1", print_r($va_item_info, true))); // handle other settings and plugin hooks $vs_default = $t_exporter_item->getSetting('default'); $vs_prefix = $t_exporter_item->getSetting('prefix'); $vs_suffix = $t_exporter_item->getSetting('suffix'); //$vs_regexp = $t_exporter_item->getSetting('filterByRegExp'); // Deprecated -- remove? $vn_max_length = $t_exporter_item->getSetting('maxLength'); $vs_skip_if_expr = $t_exporter_item->getSetting('skipIfExpression'); $vs_original_values = $t_exporter_item->getSetting('original_values'); $vs_replacement_values = $t_exporter_item->getSetting('replacement_values'); $va_replacements = ca_data_exporter_items::getReplacementArray($vs_original_values, $vs_replacement_values); foreach ($va_item_info as $vn_key => &$va_item) { $this->opo_app_plugin_manager->hookExportItemBeforeSettings(array('instance' => $t_instance, 'exporter_item_instance' => $t_exporter_item, 'export_item' => &$va_item)); // handle dontReturnValueIfOnSameDayAsStart if (caGetOption('dontReturnValueIfOnSameDayAsStart', $va_get_options, false)) { if (strlen($va_item['text']) < 1) { unset($va_item_info[$vn_key]); } } // handle skipIfExpression setting if ($vs_skip_if_expr) { // Add current value as variable "value", accessible in expressions as ^value $va_vars = array_merge(array('value' => $va_item['text']), ca_data_exporters::$s_variables); if (ExpressionParser::evaluate($vs_skip_if_expr, $va_vars)) { unset($va_item_info[$vn_key]); continue; } } // filter by regex (deprecated since you can do the same thing and more with skipIfExpression) -- remove? //if((strlen($va_item['text'])>0) && $vs_regexp) { // if(!preg_match("!".$vs_regexp."!i", $va_item['text'])) { // unset($va_item_info[$vn_key]); // continue; // } //} // do replacements $va_item['text'] = ca_data_exporter_items::replaceText($va_item['text'], $va_replacements); // if text is empty, fill in default // if text isn't empty, respect prefix and suffix if (strlen($va_item['text']) == 0) { if ($vs_default) { $va_item['text'] = $vs_default; } } else { if (strlen($vs_prefix) > 0 || strlen($vs_suffix) > 0) { $va_item['text'] = $vs_prefix . $va_item['text'] . $vs_suffix; } } if ($vn_max_length && strlen($va_item['text']) > $vn_max_length) { $va_item['text'] = substr($va_item['text'], 0, $vn_max_length) . " ..."; } // if this is a variable, set the value and delete it from the export tree $va_matches = array(); if (preg_match("/^_VARIABLE_:(.*)\$/", $va_item['element'], $va_matches)) { ca_data_exporters::$s_variables[$va_matches[1]] = $va_item['text']; unset($va_item_info[$vn_key]); continue; } // if returned value is null then we skip the item $this->opo_app_plugin_manager->hookExportItem(array('instance' => $t_instance, 'exporter_item_instance' => $t_exporter_item, 'export_item' => &$va_item)); } $o_log->logInfo(_t("Extracted data for this mapping is as follows:")); foreach ($va_item_info as $va_tmp) { $o_log->logInfo(sprintf(" element:%-20s value: %-10s", $va_tmp['element'], $va_tmp['text'])); } $va_children = $t_exporter_item->getHierarchyChildren(); if (is_array($va_children) && sizeof($va_children) > 0) { $o_log->logInfo(_t("Now proceeding to process %1 direct children in the mapping hierarchy", sizeof($va_children))); foreach ($va_children as $va_child) { foreach ($va_item_info as &$va_info) { $va_child_export = $this->processExporterItem($va_child['item_id'], $pn_table_num, $pn_record_id, $pa_options); $va_info['children'] = array_merge((array) $va_info['children'], $va_child_export); } } } return $va_item_info; }
/** * Processes single exporter item for a given record * * @param int $pn_item_id Primary of exporter item * @param int $pn_table_num Table num of item to export * @param int $pn_record_id Primary key value of item to export * @param array $pa_options * ignoreContext = don't switch context even though context may be set for current item * relationship_type_id, relationship_type_code, relationship_typename = * if this export is a sub-export (context-switch), we have no way of knowing the relationship * to the 'parent' element in the export, so there has to be a means to pass it down to make it accessable * logger = KLogger instance to use for logging. This option is mandatory! * @return array Item info */ public function processExporterItem($pn_item_id, $pn_table_num, $pn_record_id, $pa_options = array()) { $o_log = caGetOption('logger', $pa_options); // always set by exportRecord() $vb_ignore_context = caGetOption('ignoreContext', $pa_options); $o_log->logInfo(_t("Export mapping processor called with parameters [exporter_item_id:%1 table_num:%2 record_id:%3]", $pn_item_id, $pn_table_num, $pn_record_id)); $t_exporter_item = ca_data_exporters::loadExporterItemByID($pn_item_id); $t_instance = ca_data_exporters::loadInstanceByID($pn_record_id, $pn_table_num); // switch context to a different set of records if necessary and repeat current exporter item for all those selected records // (e.g. hierarchy children or related items in another table, restricted by types or relationship types) if (!$vb_ignore_context && ($vs_context = $t_exporter_item->getSetting('context'))) { $va_restrict_to_types = $t_exporter_item->getSetting('restrictToTypes'); $va_restrict_to_rel_types = $t_exporter_item->getSetting('restrictToRelationshipTypes'); $va_check_access = $t_exporter_item->getSetting('checkAccess'); $vn_new_table_num = $this->getAppDatamodel()->getTableNum($vs_context); $vb_context_is_related_table = false; if ($vn_new_table_num) { // switch to new table $vs_key = $this->getAppDatamodel()->getTablePrimaryKeyName($vs_context); } else { // this table, i.e. hierarchy context switch $vs_key = $t_instance->primaryKey(); $vn_new_table_num = $pn_table_num; } $o_log->logInfo(_t("Initiating context switch to '%1' for mapping ID %2 and record ID %3. The processor now tries to find matching records for the switch and calls himself for each of those items.", $vs_context, $pn_item_id, $pn_record_id)); switch ($vs_context) { case 'children': $va_related = $t_instance->getHierarchyChildren(); break; case 'parent': $va_related = array(); if ($vs_parent_id_fld = $t_instance->getProperty("HIERARCHY_PARENT_ID_FLD")) { $va_related[] = array($vs_key => $t_instance->get($vs_parent_id_fld)); } break; case 'ancestors': $va_parents = $t_instance->getHierarchyAncestors(null, array('idsOnly' => true)); $va_related = array(); foreach (array_unique($va_parents) as $vn_pk) { $va_related[] = array($vs_key => intval($vn_pk)); } break; case 'ca_sets': $t_set = new ca_sets(); $va_set_options = array(); if (isset($va_restrict_to_types[0])) { // the utility used below doesn't support passing multiple types so we just pass the first. // this should be enough for 99.99% of the actual use cases anyway $va_set_options['setType'] = $va_restrict_to_types[0]; } $va_set_options['checkAccess'] = $va_check_access; $va_set_options['setIDsOnly'] = true; $va_set_ids = $t_set->getSetsForItem($pn_table_num, $t_instance->getPrimaryKey(), $va_set_options); $va_related = array(); foreach (array_unique($va_set_ids) as $vn_pk) { $va_related[] = array($vs_key => intval($vn_pk)); } break; default: // plain old related table if ($vn_new_table_num) { $va_related = $t_instance->getRelatedItems($vs_context, array('restrictToTypes' => $va_restrict_to_types, 'restrictToRelationshipTypes' => $va_restrict_to_rel_types, 'checkAccess' => $va_check_access)); $vb_context_is_related_table = true; } else { return array(); } break; } $va_info = array(); if (is_array($va_related)) { $o_log->logDebug(_t("The current mapping will now be repreated for these items: %1", print_r($va_related, true))); foreach ($va_related as $va_rel) { // if we're dealing with a related table, pass on some info the relationship type to the context-switched invocation of processExporterItem(), // because we can't access that information from the related item simply because we don't exactly know where the call originated if ($vb_context_is_related_table) { $pa_options['relationship_typename'] = $va_rel['relationship_typename']; $pa_options['relationship_type_code'] = $va_rel['relationship_type_code']; $pa_options['relationship_type_id'] = $va_rel['relationship_type_id']; } $va_rel_export = $this->processExporterItem($pn_item_id, $vn_new_table_num, $va_rel[$vs_key], array_merge(array('ignoreContext' => true), $pa_options)); $va_info = array_merge($va_info, $va_rel_export); } } else { $o_log->logDebug(_t("No matching related items found for last context switch")); } return $va_info; } // end switch context // Don't prevent context switches for children of context-switched exporter items. This way you can // build cascades for jobs like exporting objects related to the creator of the record in question. unset($pa_options['ignoreContext']); $va_item_info = array(); $vs_source = $t_exporter_item->get('source'); $vs_element = $t_exporter_item->get('element'); $vb_repeat = $t_exporter_item->getSetting('repeat_element_for_multiple_values'); // if omitIfEmpty is set and get() returns nothing, we ignore this exporter item and all children if ($vs_omit_if_empty = $t_exporter_item->getSetting('omitIfEmpty')) { if (!(strlen($t_instance->get($vs_omit_if_empty)) > 0)) { return array(); } } // always return URL for export, not an HTML tag $va_get_options = array('returnURL' => true); if ($t_exporter_item->getSetting('convertCodesToDisplayText')) { $va_get_options['convertCodesToDisplayText'] = true; } if ($t_exporter_item->getSetting('returnIdno')) { $va_get_options['returnIdno'] = true; } if ($vs_delimiter = $t_exporter_item->getSetting("delimiter")) { $va_get_options['delimiter'] = $vs_delimiter; } if ($vs_template = $t_exporter_item->getSetting('template')) { $va_get_options['template'] = $vs_template; } if ($vs_locale = $t_exporter_item->getSetting('locale')) { // the global UI locale for some reason has a higher priority // than the locale setting in BaseModelWithAttributes::get // which is why we unset it here and restore it later global $g_ui_locale; $vs_old_ui_locale = $g_ui_locale; $g_ui_locale = null; $va_get_options['locale'] = $vs_locale; } if ($vs_source) { $o_log->logDebug(_t("Source for current mapping is %1", $vs_source)); $va_matches = array(); // CONSTANT value if (preg_match("/^_CONSTANT_:(.*)\$/", $vs_source, $va_matches)) { $o_log->logDebug(_t("This is a constant. Value for this mapping is '%1'", trim($va_matches[1]))); $va_item_info[] = array('text' => trim($va_matches[1]), 'element' => $vs_element); } else { if (in_array($vs_source, array("relationship_type_id", "relationship_type_code", "relationship_typename"))) { if (isset($pa_options[$vs_source]) && strlen($pa_options[$vs_source]) > 0) { $o_log->logDebug(_t("Source refers to releationship type information. Value for this mapping is '%1'", $pa_options[$vs_source])); $va_item_info[] = array('text' => $pa_options[$vs_source], 'element' => $vs_element); } } else { if (!$vb_repeat) { $vs_get = $t_instance->get($vs_source, $va_get_options); $o_log->logDebug(_t("Source is a simple get() for some bundle. Value for this mapping is '%1'", $vs_get)); $o_log->logDebug(_t("get() options are: %1", print_r($va_get_options, true))); $va_item_info[] = array('text' => $vs_get, 'element' => $vs_element); } else { // if user wants current element repeated in case of multiple returned values, go ahead and do that $va_get_options['delimiter'] = ';#;'; $vs_values = $t_instance->get($vs_source, $va_get_options); $o_log->logDebug(_t("Source is a get() that should be repeated for multiple values. Value for this mapping is '%1'. It includes the custom delimiter ';#;' that is later used to split the value into multiple values.", $vs_values)); $o_log->logDebug(_t("get() options are: %1", print_r($va_get_options, true))); $va_tmp = explode(";#;", $vs_values); foreach ($va_tmp as $vs_text) { $va_item_info[] = array('element' => $vs_element, 'text' => $vs_text); } } } } } else { if ($vs_template) { // templates without source are probably just static text, but you never know // -> run them through processor anyways $vs_proc_template = caProcessTemplateForIDs($vs_template, $pn_table_num, array($pn_record_id), array()); $o_log->logDebug(_t("Current mapping has no source but a template '%1'. Value from extracted via template processor is '%2'", $vs_template, $vs_proc_template)); $va_item_info[] = array('element' => $vs_element, 'text' => $vs_proc_template); } else { // no source, no template -> probably wrapper $o_log->logDebug(_t("Current mapping has no source and no template and is probably an XML/MARC wrapper element")); $va_item_info[] = array('element' => $vs_element); } } // reset UI locale if we unset it if ($vs_locale) { $g_ui_locale = $vs_old_ui_locale; } $o_log->logDebug(_t("We're now processing other settings like default, prefix, suffix, skipIfExpression, filterByRegExp, maxLength, plugins and replacements for this mapping")); $o_log->logDebug(_t("Local data before processing is: %1", print_r($va_item_info, true))); // handle other settings and plugin hooks $vs_default = $t_exporter_item->getSetting('default'); $vs_prefix = $t_exporter_item->getSetting('prefix'); $vs_suffix = $t_exporter_item->getSetting('suffix'); $vs_regexp = $t_exporter_item->getSetting('filterByRegExp'); $vn_max_length = $t_exporter_item->getSetting('maxLength'); $vs_skip_if_expr = $t_exporter_item->getSetting('skipIfExpression'); $vs_original_values = $t_exporter_item->getSetting('original_values'); $vs_replacement_values = $t_exporter_item->getSetting('replacement_values'); $va_replacements = ca_data_exporter_items::getReplacementArray($vs_original_values, $vs_replacement_values); foreach ($va_item_info as $vn_key => &$va_item) { // if returned value from plugin is null then we skip the item if (is_null($va_plugin_item = $this->opo_app_plugin_manager->hookExportItemBeforeSettings(array('instance' => $t_instance, 'exporter_item_instance' => $t_exporter_item, 'export_item' => $va_item)))) { continue; } else { $va_item = $va_plugin_item['export_item']; } // handle skipIfExpression setting if ($vs_skip_if_expr) { // Add current value as variable "value", accessible in expressions as ^value if (ExpressionParser::evaluate($vs_skip_if_expr, array_merge(array('value' => $va_item['text']), ca_data_exporters::$s_variables))) { unset($va_item_info[$vn_key]); continue; } } // filter by regex (deprecated since you can do the same thing and more with skipIfExpression) if (strlen($va_item['text']) > 0 && $vs_regexp) { if (!preg_match("!" . $vs_regexp . "!", $va_item['text'])) { unset($va_item_info[$vn_key]); continue; } } // do replacements $va_item['text'] = ca_data_exporter_items::replaceText($va_item['text'], $va_replacements); // if text is empty, fill in default // if text isn't empty, respect prefix and suffix if (strlen($va_item['text']) == 0) { if ($vs_default) { $va_item['text'] = $vs_default; } } else { if (strlen($vs_prefix) > 0 || strlen($vs_suffix) > 0) { $va_item['text'] = $vs_prefix . $va_item['text'] . $vs_suffix; } } if ($vn_max_length && strlen($va_item['text']) > $vn_max_length) { $va_item['text'] = substr($va_item['text'], 0, $vn_max_length) . " ..."; } // if this is a variable, set the value and delete it from the export tree $va_matches = array(); if (preg_match("/^_VARIABLE_:(.*)\$/", $va_item['element'], $va_matches)) { ca_data_exporters::$s_variables[$va_matches[1]] = $va_item['text']; unset($va_item_info[$vn_key]); continue; } // if returned value is null then we skip the item if (is_null($va_plugin_item = $this->opo_app_plugin_manager->hookExportItem(array('instance' => $t_instance, 'exporter_item_instance' => $t_exporter_item, 'export_item' => $va_item)))) { continue; } else { $va_item = $va_plugin_item['export_item']; } } $o_log->logInfo(_t("Extracted data for this mapping is as follows:")); foreach ($va_item_info as $va_tmp) { $o_log->logInfo(sprintf(" element:%-20s value: %-10s", $va_tmp['element'], $va_tmp['text'])); } $va_children = $t_exporter_item->getHierarchyChildren(); if (is_array($va_children) && sizeof($va_children) > 0) { $o_log->logInfo(_t("Now proceeding to process %1 direct children in the mapping hierarchy", sizeof($va_children))); foreach ($va_children as $va_child) { foreach ($va_item_info as &$va_info) { $va_child_export = $this->processExporterItem($va_child['item_id'], $pn_table_num, $pn_record_id, $pa_options); $va_info['children'] = array_merge((array) $va_info['children'], $va_child_export); } } } return $va_item_info; }