/** * 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; }
/** * Log a change * * @access private * @param string $ps_change_type 'I', 'U' or 'D', meaning INSERT, UPDATE or DELETE * @param int $pn_user_id user identifier, defaults to null */ protected function logChange($ps_change_type, $pn_user_id = null) { if (defined('__CA_DONT_LOG_CHANGES__')) { return null; } if (!$this->logChanges()) { return null; } $vb_is_metadata = $vb_is_metadata_value = false; if ($this->tableName() == 'ca_attributes') { $vb_log_changes_to_self = false; $va_subject_config = null; $vb_is_metadata = true; } elseif ($this->tableName() == 'ca_attribute_values') { $vb_log_changes_to_self = false; $va_subject_config = null; $vb_is_metadata_value = true; } else { $vb_log_changes_to_self = $this->getProperty('LOG_CHANGES_TO_SELF'); $va_subject_config = $this->getProperty('LOG_CHANGES_USING_AS_SUBJECT'); } global $AUTH_CURRENT_USER_ID; if (!$pn_user_id) { $pn_user_id = $AUTH_CURRENT_USER_ID; } if (!$pn_user_id) { $pn_user_id = null; } if (!in_array($ps_change_type, array('I', 'U', 'D'))) { return false; } // invalid change type (shouldn't happen) if (!($vn_row_id = $this->getPrimaryKey())) { return false; } // no logging without primary key value // get unit id (if set) global $g_change_log_unit_id; $vn_unit_id = $g_change_log_unit_id; if (!$vn_unit_id) { $vn_unit_id = null; } // get subject ids $va_subjects = array(); if ($vb_is_metadata) { // special case for logging attribute changes if (($vn_id = $this->get('row_id')) > 0) { $va_subjects[$this->get('table_num')][] = $vn_id; } } elseif ($vb_is_metadata_value) { // special case for logging metadata changes $t_attr = new ca_attributes($this->get('attribute_id')); if (($vn_id = $t_attr->get('row_id')) > 0) { $va_subjects[$t_attr->get('table_num')][] = $vn_id; } } else { if (is_array($va_subject_config)) { if (is_array($va_subject_config['FOREIGN_KEYS'])) { foreach ($va_subject_config['FOREIGN_KEYS'] as $vs_field) { $va_relationships = $this->_DATAMODEL->getManyToOneRelations($this->tableName(), $vs_field); if ($va_relationships['one_table']) { $vn_table_num = $this->_DATAMODEL->getTableNum($va_relationships['one_table']); if (!isset($va_subjects[$vn_table_num]) || !is_array($va_subjects[$vn_table_num])) { $va_subjects[$vn_table_num] = array(); } if (($vn_id = $this->get($vs_field)) > 0) { $va_subjects[$vn_table_num][] = $vn_id; } } } } if (is_array($va_subject_config['RELATED_TABLES'])) { $o_db = $this->getDb(); if (!isset($o_db) || !$o_db) { $o_db = new Db(); $o_db->dieOnError(false); } foreach ($va_subject_config['RELATED_TABLES'] as $vs_dest_table => $va_path_to_dest) { $t_dest = $this->_DATAMODEL->getTableInstance($vs_dest_table); if (!$t_dest) { continue; } $vn_dest_table_num = $t_dest->tableNum(); $vs_dest_primary_key = $t_dest->primaryKey(); $va_path_to_dest[] = $vs_dest_table; $vs_cur_table = $this->tableName(); $vs_sql = "SELECT " . $vs_dest_table . "." . $vs_dest_primary_key . " FROM " . $this->tableName() . "\n"; foreach ($va_path_to_dest as $vs_ltable) { $va_relations = $this->_DATAMODEL->getRelationships($vs_cur_table, $vs_ltable); $vs_sql .= "INNER JOIN {$vs_ltable} ON {$vs_cur_table}." . $va_relations[$vs_cur_table][$vs_ltable][0][0] . " = {$vs_ltable}." . $va_relations[$vs_cur_table][$vs_ltable][0][1] . "\n"; $vs_cur_table = $vs_ltable; } $vs_sql .= "WHERE " . $this->tableName() . "." . $this->primaryKey() . " = " . $this->getPrimaryKey(); if ($qr_subjects = $o_db->query($vs_sql)) { if (!isset($va_subjects[$vn_dest_table_num]) || !is_array($va_subjects[$vn_dest_table_num])) { $va_subjects[$vn_dest_table_num] = array(); } while ($qr_subjects->nextRow()) { if (($vn_id = $qr_subjects->get($vs_dest_primary_key)) > 0) { $va_subjects[$vn_dest_table_num][] = $vn_id; } } } else { print "<hr>Error in subject logging: "; print "<br>{$vs_sql}<hr>\n"; } } } else { if (($vn_id = $this->get('row_id')) > 0) { // At a minimum always log self as subject $va_subjects[$this->get('table_num')][] = $vn_id; } } } } if (!sizeof($va_subjects) && !$vb_log_changes_to_self) { return true; } if (!$this->opqs_change_log) { $o_db = $this->getDb(); $o_db->dieOnError(false); $vs_change_log_database = ''; if ($vs_change_log_database = $this->_CONFIG->get("change_log_database")) { $vs_change_log_database .= "."; } if (!($this->opqs_change_log = $o_db->prepare("\n\t\t\t\tINSERT INTO " . $vs_change_log_database . "ca_change_log\n\t\t\t\t(\n\t\t\t\t\tlog_datetime, user_id, unit_id, changetype,\n\t\t\t\t\tlogged_table_num, logged_row_id, batch_id\n\t\t\t\t)\n\t\t\t\tVALUES\n\t\t\t\t(?, ?, ?, ?, ?, ?, ?)\n\t\t\t"))) { // prepare failed - shouldn't happen return false; } if (!($this->opqs_change_log_snapshot = $o_db->prepare("\n\t\t\t\tINSERT IGNORE INTO " . $vs_change_log_database . "ca_change_log_snapshots\n\t\t\t\t(\n\t\t\t\t\tlog_id, snapshot\n\t\t\t\t)\n\t\t\t\tVALUES\n\t\t\t\t(?, ?)\n\t\t\t"))) { // prepare failed - shouldn't happen return false; } if (!($this->opqs_change_log_subjects = $o_db->prepare("\n\t\t\t\tINSERT IGNORE INTO " . $vs_change_log_database . "ca_change_log_subjects\n\t\t\t\t(\n\t\t\t\t\tlog_id, subject_table_num, subject_row_id\n\t\t\t\t)\n\t\t\t\tVALUES\n\t\t\t\t(?, ?, ?)\n\t\t\t"))) { // prepare failed - shouldn't happen return false; } } // get snapshot of changes made to record $va_snapshot = $this->getSnapshot($ps_change_type === 'U' ? true : false); $vs_snapshot = caSerializeForDatabase($va_snapshot, true); if (!($ps_change_type == 'U' && !sizeof($va_snapshot))) { // Create primary log entry global $g_change_log_batch_id; // Log batch_id as set in global by ca_batch_log model (app/models/ca_batch_log.php) $this->opqs_change_log->execute(time(), $pn_user_id, $vn_unit_id, $ps_change_type, $this->tableNum(), $vn_row_id, (int) $g_change_log_batch_id ? (int) $g_change_log_batch_id : null); $vn_log_id = $this->opqs_change_log->getLastInsertID(); $this->opqs_change_log_snapshot->execute($vn_log_id, $vs_snapshot); global $g_change_log_delegate; if ($g_change_log_delegate && method_exists($g_change_log_delegate, "onLogChange")) { call_user_func(array($g_change_log_delegate, 'onLogChange'), $this->tableNum(), $vn_row_id, $vn_log_id); } foreach ($va_subjects as $vn_subject_table_num => $va_subject_ids) { foreach ($va_subject_ids as $vn_subject_row_id) { $this->opqs_change_log_subjects->execute($vn_log_id, $vn_subject_table_num, $vn_subject_row_id); } } } return true; }
/** * Return the specific attribute with the specified attribute_id (assuming it's attached to the current row) */ public function getAttributeByID($pn_attribute_id, $pa_options = null) { if (isset($pa_options['row_id']) && $pa_options['row_id']) { $vn_row_id = $pa_options['row_id']; } else { $vn_row_id = $this->getPrimaryKey(); } if (!$vn_row_id) { return null; } $t_attr = new ca_attributes($pn_attribute_id); if (!$t_attr->getPrimaryKey()) { return false; } if ((int) $t_attr->get('row_id') !== (int) $vn_row_id) { return false; } return $t_attr->getAttributeValues(array('returnAs' => 'attributeInstance')); }
/** * */ public static function check_media_fixity($po_opts = null) { require_once __CA_LIB_DIR__ . "/core/Db.php"; require_once __CA_MODELS_DIR__ . "/ca_object_representations.php"; $ps_file_path = strtolower((string) $po_opts->getOption('file')); $ps_format = strtolower((string) $po_opts->getOption('format')); if (!in_array($ps_format, array('text', 'tab', 'csv'))) { $ps_format = 'text'; } $o_db = new Db(); $o_dm = Datamodel::load(); $t_rep = new ca_object_representations(); $vs_report_output = join($ps_format == 'tab' ? "\t" : ",", array(_t('Type'), _t('Error'), _t('Name'), _t('ID'), _t('Version'), _t('File path'), _t('Expected MD5'), _t('Actual MD5'))) . "\n"; // Verify object representations $qr_reps = $o_db->query("SELECT representation_id, idno, media FROM ca_object_representations WHERE deleted = 0"); print CLIProgressBar::start($vn_rep_count = $qr_reps->numRows(), _t('Checking object representations')) . "\n"; $vn_errors = 0; while ($qr_reps->nextRow()) { $vn_representation_id = $qr_reps->get('representation_id'); print CLIProgressBar::next(1, _t("Checking representation media %1", $vn_representation_id)); $va_media_versions = $qr_reps->getMediaVersions('media'); foreach ($va_media_versions as $vs_version) { $vs_path = $qr_reps->getMediaPath('media', $vs_version); $vs_database_md5 = $qr_reps->getMediaInfo('media', $vs_version, 'MD5'); $vs_file_md5 = md5_file($vs_path); if ($vs_database_md5 !== $vs_file_md5) { $t_rep->load($vn_representation_id); $vs_message = _t("[Object representation][MD5 mismatch] %1; version %2 [%3]", $t_rep->get("ca_objects.preferred_labels.name") . " (" . $t_rep->get("ca_objects.idno") . "); representation_id={$vn_representation_id}", $vs_version, $vs_path); switch ($ps_format) { case 'text': default: $vs_report_output .= "{$vs_message}\n"; break; case 'tab': case 'csv': $va_log = array(_t('Object representation'), "MD5 mismatch", caEscapeForDelimitedOutput($t_rep->get("ca_objects.preferred_labels.name") . " (" . $t_rep->get("ca_objects.idno") . ")"), $vn_representation_id, $vs_version, $vs_path, $vs_database_md5, $vs_file_md5); $vs_report_output .= join($ps_format == 'tab' ? "\t" : ",", $va_log) . "\n"; break; } CLIUtils::addError($vs_message); $vn_errors++; } } } print CLIProgressBar::finish(); CLIUtils::addMessage(_t('%1 errors for %2 representations', $vn_errors, $vn_rep_count)); // get all Media elements $va_elements = ca_metadata_elements::getElementsAsList(false, null, null, true, false, true, array(16)); // 16=media if (is_array($va_elements) && sizeof($va_elements)) { if (is_array($va_element_ids = caExtractValuesFromArrayList($va_elements, 'element_id', array('preserveKeys' => false))) && sizeof($va_element_ids)) { $qr_c = $o_db->query("\n\t\t\t\t\t\tSELECT count(*) c\n\t\t\t\t\t\tFROM ca_attribute_values\n\t\t\t\t\t\tWHERE\n\t\t\t\t\t\t\telement_id in (?)\n\t\t\t\t\t", array($va_element_ids)); if ($qr_c->nextRow()) { $vn_count = $qr_c->get('c'); } else { $vn_count = 0; } print CLIProgressBar::start($vn_count, _t('Checking attribute media')); $vn_errors = 0; foreach ($va_elements as $vs_element_code => $va_element_info) { $qr_vals = $o_db->query("SELECT value_id FROM ca_attribute_values WHERE element_id = ?", (int) $va_element_info['element_id']); $va_vals = $qr_vals->getAllFieldValues('value_id'); foreach ($va_vals as $vn_value_id) { $t_attr_val = new ca_attribute_values($vn_value_id); if ($t_attr_val->getPrimaryKey()) { $t_attr_val->setMode(ACCESS_WRITE); $t_attr_val->useBlobAsMediaField(true); print CLIProgressBar::next(1, _t("Checking attribute media %1", $vn_value_id)); $va_media_versions = $t_attr_val->getMediaVersions('value_blob'); foreach ($va_media_versions as $vs_version) { $vs_path = $t_attr_val->getMediaPath('value_blob', $vs_version); $vs_database_md5 = $t_attr_val->getMediaInfo('value_blob', $vs_version, 'MD5'); $vs_file_md5 = md5_file($vs_path); if ($vs_database_md5 !== $vs_file_md5) { $t_attr = new ca_attributes($vn_attribute_id = $t_attr_val->get('attribute_id')); $vs_label = "attribute_id={$vn_attribute_id}; value_id={$vn_value_id}"; if ($t_instance = $o_dm->getInstanceByTableNum($t_attr->get('table_num'), true)) { if ($t_instance->load($t_attr->get('row_id'))) { $vs_label = $t_instance->get($t_instance->tableName() . '.preferred_labels'); if ($vs_idno = $t_instance->get($t_instance->getProperty('ID_NUMBERING_ID_FIELD'))) { $vs_label .= " ({$vs_label})"; } } } $vs_message = _t("[Media attribute][MD5 mismatch] %1; value_id=%2; version %3 [%4]", $vs_label, $vn_value_id, $vs_version, $vs_path); switch ($ps_format) { case 'text': default: $vs_report_output .= "{$vs_message}\n"; break; case 'tab': case 'csv': $va_log = array(_t('Media attribute'), _t("MD5 mismatch"), caEscapeForDelimitedOutput($vs_label), $vn_value_id, $vs_version, $vs_path, $vs_database_md5, $vs_file_md5); $vs_report_output .= join($ps_format == 'tab' ? "\t" : ",", $va_log); break; } CLIUtils::addError($vs_message); $vn_errors++; } } } } } print CLIProgressBar::finish(); CLIUtils::addMessage(_t('%1 errors for %2 attributes', $vn_errors, $vn_rep_count)); } } // get all File elements $va_elements = ca_metadata_elements::getElementsAsList(false, null, null, true, false, true, array(15)); // 15=file if (is_array($va_elements) && sizeof($va_elements)) { if (is_array($va_element_ids = caExtractValuesFromArrayList($va_elements, 'element_id', array('preserveKeys' => false))) && sizeof($va_element_ids)) { $qr_c = $o_db->query("\n\t\t\t\t\t\tSELECT count(*) c\n\t\t\t\t\t\tFROM ca_attribute_values\n\t\t\t\t\t\tWHERE\n\t\t\t\t\t\t\telement_id in (?)\n\t\t\t\t\t", array($va_element_ids)); if ($qr_c->nextRow()) { $vn_count = $qr_c->get('c'); } else { $vn_count = 0; } print CLIProgressBar::start($vn_count, _t('Checking attribute files')); $vn_errors = 0; foreach ($va_elements as $vs_element_code => $va_element_info) { $qr_vals = $o_db->query("SELECT value_id FROM ca_attribute_values WHERE element_id = ?", (int) $va_element_info['element_id']); $va_vals = $qr_vals->getAllFieldValues('value_id'); foreach ($va_vals as $vn_value_id) { $t_attr_val = new ca_attribute_values($vn_value_id); if ($t_attr_val->getPrimaryKey()) { $t_attr_val->setMode(ACCESS_WRITE); $t_attr_val->useBlobAsFileField(true); print CLIProgressBar::next(1, _t("Checking attribute file %1", $vn_value_id)); $vs_path = $t_attr_val->getFilePath('value_blob'); $vs_database_md5 = $t_attr_val->getFileInfo('value_blob', 'MD5'); $vs_file_md5 = md5_file($vs_path); if ($vs_database_md5 !== $vs_file_md5) { $t_attr = new ca_attributes($vn_attribute_id = $t_attr_val->get('attribute_id')); $vs_label = "attribute_id={$vn_attribute_id}; value_id={$vn_value_id}"; if ($t_instance = $o_dm->getInstanceByTableNum($t_attr->get('table_num'), true)) { if ($t_instance->load($t_attr->get('row_id'))) { $vs_label = $t_instance->get($t_instance->tableName() . '.preferred_labels'); if ($vs_idno = $t_instance->get($t_instance->getProperty('ID_NUMBERING_ID_FIELD'))) { $vs_label .= " ({$vs_label})"; } } } $vs_message = _t("[File attribute][MD5 mismatch] %1; value_id=%2; version %3 [%4]", $vs_label, $vn_value_id, $vs_version, $vs_path); switch ($ps_format) { case 'text': default: $vs_report_output .= "{$vs_message}\n"; break; case 'tab': case 'csv': $va_log = array(_t('File attribute'), _t("MD5 mismatch"), caEscapeForDelimitedOutput($vs_label), $vn_value_id, $vs_version, $vs_path, $vs_database_md5, $vs_file_md5); $vs_report_output .= join($ps_format == 'tab' ? "\t" : ",", $va_log); break; } CLIUtils::addError($vs_message); $vn_errors++; } } } } print CLIProgressBar::finish(); CLIUtils::addMessage(_t('%1 errors for %2 attributes', $vn_errors, $vn_rep_count)); } } if ($ps_file_path) { file_put_contents($ps_file_path, $vs_report_output); } return true; }
/** * Fetch details on an item from a remote data source and output results of the 'display' key in the response. * */ public function GetDetail() { $pn_element_id = $this->request->getParameter('element_id', pInteger); $t_element = new ca_metadata_elements($pn_element_id); $va_data = array(); if (!$t_element->getPrimaryKey()) { // error $va_items['error'] = array('label' => _t('ERROR: Invalid element_id'), 'idno' => ''); } else { $vs_service = $t_element->getSetting('service'); $va_settings = $t_element->getSettings(); $pn_attribute_id = $this->request->getParameter('id', pInteger); $t_attr_val = new ca_attribute_values(); if ($t_attr_val->load(array('attribute_id' => $pn_attribute_id, 'element_id' => $pn_element_id))) { $t_attr = new ca_attributes(); if ($t_attr->load($pn_attribute_id)) { if (!caCanRead($this->request->getUserID(), $t_attr->get('table_num'), $t_attr->get('row_id'), $t_element->get('element_code'))) { $va_items['error'] = array('label' => _t('ERROR: You do not have access to this item'), 'idno' => ''); } else { $vs_url = $t_attr_val->get('value_longtext2'); if (!($o_plugin = InformationServiceManager::getInformationServiceInstance($vs_service))) { $va_items['error'] = array('label' => _t('ERROR: Invalid service'), 'idno' => ''); } else { $vs_cache_key = md5(print_r($va_settings, true) . $vs_url); if (CompositeCache::contains($vs_cache_key, 'InformationServiceExtendedInfo')) { $va_data = CompositeCache::fetch($vs_cache_key, 'InformationServiceExtendedInfo'); } else { $va_data = $o_plugin->getExtendedInformation($va_settings, $vs_url); CompositeCache::save($vs_cache_key, $va_data, 'InformationServiceExtendedInfo'); } } } } } } $this->view->setVar('detail', $va_data); return $this->render('ajax_information_service_detail_html.php'); }
/** * Initiates user download of media stored in a media attribute, returning file in response to request. * Adds download output to response directly. No view is used. * * @param array $pa_options Array of options passed through to _initView */ public function DownloadAttributeMedia($pa_options = null) { if (!($pn_value_id = $this->request->getParameter('value_id', pInteger))) { return; } $t_attr_val = new ca_attribute_values($pn_value_id); if (!$t_attr_val->getPrimaryKey()) { return; } $t_attr = new ca_attributes($t_attr_val->get('attribute_id')); $vn_table_num = $this->opo_datamodel->getTableNum($this->ops_table_name); if ($t_attr->get('table_num') != $vn_table_num) { $this->response->setRedirect($this->request->config->get('error_display_url') . '/n/2580?r=' . urlencode($this->request->getFullUrlPath())); return; } $t_element = new ca_metadata_elements($t_attr->get('element_id')); $this->request->setParameter($this->opo_datamodel->getTablePrimaryKeyName($vn_table_num), $t_attr->get('row_id')); list($vn_subject_id, $t_subject) = $this->_initView($pa_options); $ps_version = $this->request->getParameter('version', pString); if (!$this->_checkAccess($t_subject)) { return false; } // // Does user have access to bundle? // if ($this->request->user->getBundleAccessLevel($this->ops_table_name, $t_element->get('element_code')) < __CA_BUNDLE_ACCESS_READONLY__) { $this->response->setRedirect($this->request->config->get('error_display_url') . '/n/2580?r=' . urlencode($this->request->getFullUrlPath())); return; } $t_attr_val->useBlobAsMediaField(true); if (!in_array($ps_version, $t_attr_val->getMediaVersions('value_blob'))) { $ps_version = 'original'; } $o_view = new View($this->request, $this->request->getViewsDirectoryPath() . '/bundles/'); // get value $t_element = new ca_metadata_elements($t_attr_val->get('element_id')); // check that value is a media attribute if ($t_element->get('datatype') != 16) { // 16=media return; } $vs_path = $t_attr_val->getMediaPath('value_blob', $ps_version); $vs_path_ext = pathinfo($vs_path, PATHINFO_EXTENSION); if ($vs_name = trim($t_attr_val->get('value_longtext2'))) { $vs_file_name = pathinfo($vs_name, PATHINFO_FILENAME); $vs_name = "{$vs_file_name}.{$vs_path_ext}"; } else { $vs_name = _t("downloaded_file.%1", $vs_path_ext); } $o_view->setVar('file_path', $vs_path); $o_view->setVar('file_name', $vs_name); // send download $this->response->addContent($o_view->render('ca_attributes_download_media.php')); }
/** * remove attribute from current row */ public function _removeAttribute($pn_attribute_id, $po_trans = null, $pa_options = null) { $t_attr = new ca_attributes($pn_attribute_id); $t_attr->purify($this->purify()); if ($po_trans) { $t_attr->setTransaction($po_trans); } if (!$t_attr->getPrimaryKey() || $t_attr->get('table_num') != $this->tableNum() || $this->getPrimaryKey() != $t_attr->get('row_id')) { $this->postError(1969, _t('Can\'t edit invalid attribute'), 'BaseModelWithAttributes->editAttribute()', $pa_options['error_source']); return false; } if (!$t_attr->removeAttribute()) { foreach ($t_attr->errors as $o_error) { $this->postError($o_error->getErrorNumber(), $o_error->getErrorDescription(), $o_error->getErrorContext(), $pa_options['error_source']); } return false; } //$this->setFieldValuesArray($this->addAttributesToFieldValuesArray()); return true; }