/** * Outputs the form for mapping columns to the import fields. * @param array $options Options array passed to the import control. */ private static function upload_mappings_form($options) { ini_set('auto_detect_line_endings', 1); if (!file_exists($_SESSION['uploaded_file'])) { return lang::get('upload_not_available'); } self::add_resource('jquery_ui'); $filename = basename($_SESSION['uploaded_file']); // If the last step was skipped because the user did not have any settings to supply, presetSettings contains the presets. // Otherwise we'll use the settings form content which already in $_POST so will overwrite presetSettings. if (isset($options['presetSettings'])) { $settings = array_merge($options['presetSettings'], $_POST); } else { $settings = $_POST; } // only want defaults that actually have a value - others can be set on a per-row basis by mapping to a column foreach ($settings as $key => $value) { if (empty($value)) { unset($settings[$key]); } } // cache the mappings $metadata = array('settings' => json_encode($settings)); $post = array_merge($options['auth']['write_tokens'], $metadata); $request = parent::$base_url . "index.php/services/import/cache_upload_metadata?uploaded_csv={$filename}"; $response = self::http_post($request, $post); if (!isset($response['output']) || $response['output'] != 'OK') { return "Could not upload the settings metadata. <br/>" . print_r($response, true); } $request = parent::$base_url . "index.php/services/import/get_import_fields/" . $options['model']; $request .= '?' . self::array_to_query_string($options['auth']['read']); // include survey and website information in the request if available, as this limits the availability of custom attributes if (!empty($settings['website_id'])) { $request .= '&website_id=' . trim($settings['website_id']); } if (!empty($settings['survey_id'])) { $request .= '&survey_id=' . trim($settings['survey_id']); } if (!empty($settings['useAssociations']) && $settings['useAssociations']) { $request .= '&use_associations=true'; } $response = self::http_post($request, array()); $fields = json_decode($response['output'], true); if (!is_array($fields)) { return "curl request to {$request} failed. Response " . print_r($response, true); } $request = str_replace('get_import_fields', 'get_required_fields', $request); $response = self::http_post($request); $responseIds = json_decode($response['output'], true); if (!is_array($responseIds)) { return "curl request to {$request} failed. Response " . print_r($response, true); } $model_required_fields = self::expand_ids_to_fks($responseIds); if (!empty($settings)) { $preset_fields = self::expand_ids_to_fks(array_keys($settings)); } else { $preset_fields = array(); } if (!empty($preset_fields)) { $unlinked_fields = array_diff_key($fields, array_combine($preset_fields, $preset_fields)); } else { $unlinked_fields = $fields; } // only use the required fields that are available for selection - the rest are handled somehow else $unlinked_required_fields = array_intersect($model_required_fields, array_keys($unlinked_fields)); $handle = fopen($_SESSION['uploaded_file'], "r"); $columns = fgetcsv($handle, 1000, ","); $reload = self::get_reload_link_parts(); $reloadpath = $reload['path'] . '?' . self::array_to_query_string($reload['params']); self::clear_website_survey_fields($unlinked_fields, $settings); self::clear_website_survey_fields($unlinked_required_fields, $settings); $savedFieldMappings = array(); //get the user's checked preference for the import page if (function_exists('hostsite_get_user_field')) { $json = hostsite_get_user_field('import_field_mappings'); if ($json === false) { if (!hostsite_set_user_field('import_field_mappings', '[]')) { self::$rememberingMappings = false; } } else { $json = trim($json); $savedFieldMappings = json_decode(trim($json), true); } } else { // host does not support user profiles, so we can't remember mappings self::$rememberingMappings = false; } // if the user checked the Remember All checkbox need to remember this setting $checkedRememberAll = isset($savedFieldMappings['RememberAll']) ? ' checked="checked"' : ''; $r = "<form method=\"post\" id=\"entry_form\" action=\"{$reloadpath}\" class=\"iform\">\n" . '<p>' . lang::get('column_mapping_instructions') . '</p>' . '<div class="ui-helper-clearfix import-mappings-table"><table class="ui-widget ui-widget-content">' . '<thead class="ui-widget-header">' . "<tr><th>" . lang::get('Column in CSV File') . "</th><th>" . lang::get('Maps to attribute') . "</th>"; if (self::$rememberingMappings) { $r .= "<th id='remember-all-header' name='remember-all-header'>" . lang::get('Remember choice?') . "<br/><input type='checkbox' name='RememberAll' id='RememberAll' value='1' title='" . lang::get('Tick all boxes to remember every column mapping next time you import.') . "'{$checkedRememberAll}/></th>"; self::$javascript .= "\$('#RememberAll').change(function() {\n if (this.checked) {\n \$(\".rememberField\").attr(\"checked\",\"checked\")\n } else {\n \$(\".rememberField\").removeAttr(\"checked\")\n }\n});\n"; } $r .= '</tr></thead><tbody>'; foreach ($columns as $column) { $colFieldName = preg_replace('/[^A-Za-z0-9]/', '_', $column); $r .= "<tr><td>{$column}</td><td><select name=\"{$colFieldName}\" id=\"{$colFieldName}\">"; $r .= self::get_column_options($options['model'], $unlinked_fields, $column, ' ', $savedFieldMappings); $r .= "</select></td></tr>\n"; } $r .= '</tbody>'; $r .= '</table>'; $r .= '<div id="required-instructions" class="import-mappings-instructions"><h2>' . lang::get('Tasks') . '</h2><span>' . lang::get('The following database attributes must be matched to a column in your import file before you can continue') . ':</span><ul></ul><br/></div>'; $r .= '<div id="duplicate-instructions" class="import-mappings-instructions"><span id="duplicate-instruct">' . lang::get('There are currently two or more drop-downs allocated to the same value.') . '</span><ul></ul><br/></div></div>'; $r .= '<input type="hidden" name="import_step" value="2" />'; $r .= '<input type="submit" name="submit" id="submit" value="' . lang::get('Upload') . '" class="ui-corner-all ui-state-default button" />'; $r .= '</form>'; self::$javascript .= "function detect_duplicate_fields() {\n var valueStore = [];\n var duplicateStore = [];\n var valueStoreIndex = 0;\n var duplicateStoreIndex = 0;\n \$.each(\$('#entry_form select'), function(i, select) {\n if (valueStoreIndex==0) {\n valueStore[valueStoreIndex] = select.value;\n valueStoreIndex++;\n } else {\n for(i=0; i<valueStoreIndex; i++) {\n if (select.value==valueStore[i] && select.value != '<" . lang::get('Not imported') . ">') {\n duplicateStore[duplicateStoreIndex] = select.value;\n duplicateStoreIndex++;\n }\n \n }\n valueStore[valueStoreIndex] = select.value;\n valueStoreIndex++;\n } \n })\n if (duplicateStore.length==0) {\n DuplicateAllowsUpload = 1;\n \$('#duplicate-instruct').css('display', 'none');\n } else {\n DuplicateAllowsUpload = 0;\n \$('#duplicate-instruct').css('display', 'inline');\n }\n }\n"; self::$javascript .= "function update_required_fields() {\n // copy the list of required fields\n var fields = \$.extend(true, {}, required_fields),\n sampleVagueDates = [],\n \t locationReference = false,\n fieldTokens, thisValue;\n \$('#required-instructions li').remove();\n // strip out the ones we have already allocated\n \$.each(\$('#entry_form select'), function(i, select) {\n thisValue = select.value;\n // If there are several options of how to search a single lookup then they\n // are identified by a 3rd token, e.g. occurrence:fk_taxa_taxon_list:search_code.\n // These cases fulfil the needs of a required field so we can remove them.\n fieldTokens = thisValue.split(':');\n if (fieldTokens.length>2) {\n fieldTokens.pop();\n thisValue = fieldTokens.join(':');\n }\n delete fields[thisValue];\n // special case for vague dates - if we have a complete sample vague date, then can strike out the sample:date required field\n if (select.value.substr(0,12)=='sample:date_') {\n sampleVagueDates.push(thisValue);\n }\n \t// and another special case for samples: can either include the sref or a foreign key reference to a location.\n \tif (select.value.substr(0,18)=='sample:fk_location') { // catches the code based fk as well\n locationReference = true;\n }\n });\n if (sampleVagueDates.length==3) {\n // got a full vague date, so can remove the required date field\n delete fields['sample:date'];\n }\n if (locationReference) {\n // got a location foreign key reference, so can remove the required entered sref fields\n delete fields['sample:entered_sref'];\n \tdelete fields['sample:entered_sref_system']\n }\n var output = '';\n \$.each(fields, function(field, caption) {\n output += '<li>'+caption+'</li>';\n });\n if (output==='') {\n \$('#required-instructions').css('display', 'none');\n RequiredAllowsUpload = 1;\n } else {\n \$('#required-instructions').css('display', 'inline');\n RequiredAllowsUpload = 0;\n }\n if (RequiredAllowsUpload == 1 && DuplicateAllowsUpload == 1) {\n \$('#submit').attr('disabled', false);\n } else {\n \$('#submit').attr('disabled', true);\n }\n \$('#required-instructions ul').html(output);\n }\n"; self::$javascript .= "required_fields={};\n"; foreach ($unlinked_required_fields as $field) { $caption = $unlinked_fields[$field]; if (empty($caption)) { $tokens = explode(':', $field); $fieldname = $tokens[count($tokens) - 1]; $caption = lang::get(self::processLabel(preg_replace(array('/^fk_/', '/_id$/'), array('', ''), $fieldname))); } $caption = self::translate_field($field, $caption); self::$javascript .= "required_fields['{$field}']='{$caption}';\n"; } self::$javascript .= "detect_duplicate_fields();\n"; self::$javascript .= "update_required_fields();\n"; self::$javascript .= "\$('#entry_form select').change(function() {detect_duplicate_fields(); update_required_fields();});\n"; return $r; }