public function dispatchLoopShutdown()
 {
     //
     // Force output to be sent - we need the client to have the page before
     // we start flushing progress bar updates
     //
     $app = AppController::getInstance();
     $req = $app->getRequest();
     $resp = $app->getResponse();
     $resp->sendResponse();
     $resp->clearContent();
     //
     // Do export
     //
     if ($req->isLoggedIn()) {
         set_time_limit(3600 * 24);
         // if it takes more than 24 hours we're in trouble
         $vn_id = $req->getParameter('exporter_id', pInteger);
         $vs_search = $req->getParameter('search', pString);
         $t_exporter = new ca_data_exporters($vn_id);
         $vs_file = tempnam(caGetTempDirPath(), 'export');
         if ($vn_set_id = $req->getParameter('set_id', pInteger)) {
             ca_data_exporters::exportRecordsFromSet($t_exporter->get('exporter_code'), $vn_set_id, $vs_file, array('request' => $req, 'progressCallback' => 'caIncrementBatchMetadataExportProgress'));
         } else {
             ca_data_exporters::exportRecordsFromSearchExpression($t_exporter->get('exporter_code'), $vs_search, $vs_file, array('request' => $req, 'progressCallback' => 'caIncrementBatchMetadataExportProgress'));
         }
     }
     // export done, move file to application tmp dir and create download link (separate action in the export controller)
     if (filesize($vs_file)) {
         $vs_new_filename = $vn_id . "_" . md5($vs_file);
         rename($vs_file, __CA_APP_DIR__ . '/tmp/' . $vs_new_filename);
         caExportAddDownloadLink($req, $vs_new_filename);
     }
 }
 public function dispatchLoopShutdown()
 {
     //
     // Force output to be sent - we need the client to have the page before
     // we start flushing progress bar updates
     //
     $app = AppController::getInstance();
     $req = $app->getRequest();
     $resp = $app->getResponse();
     $resp->sendResponse();
     $resp->clearContent();
     //
     // Do export
     //
     if (!$req->isLoggedIn()) {
         return;
     }
     set_time_limit(3600 * 24);
     // if it takes more than 24 hours we're in trouble
     $vn_id = $req->getParameter('exporter_id', pInteger);
     $t_exporter = new ca_data_exporters($vn_id);
     $vs_file = tempnam(__CA_APP_DIR__ . DIRECTORY_SEPARATOR . 'tmp', 'dataExport');
     // we have 3 different sources for batch exports: search/browse result, sets and search expressions (deprecated)
     // they all operate on different parameters and on different static functions in ca_data_exporters
     if ($req->getParameter('caIsExportFromSearchOrBrowseResult', pInteger)) {
         // batch export from search or browse result
         $vs_find_type = $req->getParameter('find_type', pString);
         $vo_result_context = new ResultContext($req, $t_exporter->getTargetTableName(), $vs_find_type);
         $t_instance = $t_exporter->getTargetTableInstance();
         $o_result = $t_instance->makeSearchResult($t_instance->tableName(), $vo_result_context->getResultList());
         ca_data_exporters::exportRecordsFromSearchResult($t_exporter->get('exporter_code'), $o_result, $vs_file, array('request' => $req, 'progressCallback' => 'caIncrementBatchMetadataExportProgress'));
     } else {
         if ($vn_set_id = $req->getParameter('set_id', pInteger)) {
             // batch export from set
             ca_data_exporters::exportRecordsFromSet($t_exporter->get('exporter_code'), $vn_set_id, $vs_file, array('request' => $req, 'progressCallback' => 'caIncrementBatchMetadataExportProgress'));
         } else {
             // batch export from search expression (deprecated)
             $vs_search = $req->getParameter('search', pString);
             ca_data_exporters::exportRecordsFromSearchExpression($t_exporter->get('exporter_code'), $vs_search, $vs_file, array('request' => $req, 'progressCallback' => 'caIncrementBatchMetadataExportProgress'));
         }
     }
     // export done, record it in session for later usage in download/destination action
     if (filesize($vs_file)) {
         $o_session = $req->getSession();
         $o_session->setVar('export_file', $vs_file);
         $o_session->setVar('export_content_type', $t_exporter->getContentType());
         $o_session->setVar('exporter_id', $t_exporter->getPrimaryKey());
         caExportAddDownloadLink($req);
     }
 }
 protected function _genExportWithMapping($po_result, $pn_exporter_id)
 {
     // Can user batch export?
     if (!$this->request->user->canDoAction('can_batch_export_metadata')) {
         $this->response->setRedirect($this->request->config->get('error_display_url') . '/n/3440?r=' . urlencode($this->request->getFullUrlPath()));
         return;
     }
     // Can user export records of this type?
     if (!$this->request->user->canDoAction('can_export_' . $this->ops_tablename)) {
         $this->response->setRedirect($this->request->config->get('error_display_url') . '/n/3430?r=' . urlencode($this->request->getFullUrlPath()));
         return;
     }
     $t_exporter = new ca_data_exporters($pn_exporter_id);
     if (!($t_exporter->getPrimaryKey() > 0)) {
         $this->postError(3420, _t("Could not load export mapping"), "BaseFindController->_genExportWithMapping()");
         return;
     }
     if (substr(get_class($this), 0, 6) == 'Browse') {
         $vs_export_filename = Configuration::load()->get($this->ops_tablename . '_batch_export_filename');
     } else {
         $vs_export_filename = preg_replace("/[^\\p{L}\\p{N}\\-]/", '_', $this->opo_result_context->getSearchExpression());
         $vs_export_filename = preg_replace("/[\\_]+/", '_', $vs_export_filename);
     }
     $vs_tmp_file = tempnam(caGetTempDirPath(), 'export');
     ca_data_exporters::exportRecordsFromSearchResult($t_exporter->get('exporter_code'), $po_result, $vs_tmp_file);
     header('Content-Type: ' . $t_exporter->getContentType() . '; charset=UTF-8');
     header('Content-Disposition: attachment; filename="' . $vs_export_filename . '.' . $t_exporter->getFileExtension() . '"');
     header('Content-Transfer-Encoding: binary');
     readfile($vs_tmp_file);
     @unlink($vs_tmp_file);
     exit;
 }
 /**
  * Load exporter configuration from XLSX file
  * @param string $ps_source file path for source XLSX
  * @param array $pa_errors call-by-reference array to store and "return" error messages
  * @param array $pa_options options
  * @return ca_data_exporters BaseModel representation of the new exporter. false/null if there was an error.
  */
 public static function loadExporterFromFile($ps_source, &$pa_errors, $pa_options = null)
 {
     global $g_ui_locale_id;
     $vn_locale_id = isset($pa_options['locale_id']) && (int) $pa_options['locale_id'] ? (int) $pa_options['locale_id'] : $g_ui_locale_id;
     $pa_errors = array();
     $o_excel = PHPExcel_IOFactory::load($ps_source);
     $o_sheet = $o_excel->getSheet(0);
     $vn_row = 0;
     $va_settings = array();
     $va_mappings = array();
     $va_ids = array();
     foreach ($o_sheet->getRowIterator() as $o_row) {
         if ($vn_row++ == 0) {
             // skip first row (headers)
             continue;
         }
         $vn_row_num = $o_row->getRowIndex();
         $o_cell = $o_sheet->getCellByColumnAndRow(0, $vn_row_num);
         $vs_mode = (string) $o_cell->getValue();
         switch ($vs_mode) {
             case 'Mapping':
             case 'Constant':
             case 'Variable':
             case 'RepeatMappings':
                 $o_id = $o_sheet->getCellByColumnAndRow(1, $o_row->getRowIndex());
                 $o_parent = $o_sheet->getCellByColumnAndRow(2, $o_row->getRowIndex());
                 $o_element = $o_sheet->getCellByColumnAndRow(3, $o_row->getRowIndex());
                 $o_source = $o_sheet->getCellByColumnAndRow(4, $o_row->getRowIndex());
                 $o_options = $o_sheet->getCellByColumnAndRow(5, $o_row->getRowIndex());
                 if ($vs_id = trim((string) $o_id->getValue())) {
                     $va_ids[] = $vs_id;
                 }
                 if ($vs_parent_id = trim((string) $o_parent->getValue())) {
                     if (!in_array($vs_parent_id, $va_ids) && $vs_parent_id != $vs_id) {
                         $pa_errors[] = _t("Warning: skipped mapping at row %1 because parent id was invalid", $vn_row);
                         continue 2;
                     }
                 }
                 if (!($vs_element = trim((string) $o_element->getValue()))) {
                     $pa_errors[] = _t("Warning: skipped mapping at row %1 because element was not defined", $vn_row);
                     continue 2;
                 }
                 $vs_source = trim((string) $o_source->getValue());
                 if ($vs_mode == 'Constant') {
                     if (strlen($vs_source) < 1) {
                         // ignore constant rows without value
                         continue;
                     }
                     $vs_source = "_CONSTANT_:{$vs_source}";
                 }
                 if ($vs_mode == 'Variable') {
                     if (preg_match("/^[A-Za-z0-9\\_\\-]+\$/", $vs_element)) {
                         $vs_element = "_VARIABLE_:{$vs_element}";
                     } else {
                         $pa_errors[] = _t("Variable name %1 is invalid. It should only contain ASCII letters, numbers, hyphens and underscores. The variable was not created.", $vs_element);
                         continue 2;
                     }
                 }
                 $va_options = null;
                 if ($vs_options_json = (string) $o_options->getValue()) {
                     if (is_null($va_options = @json_decode($vs_options_json, true))) {
                         $pa_errors[] = _t("Warning: options for element %1 are not in proper JSON", $vs_element);
                     }
                 }
                 $va_options['_id'] = (string) $o_id->getValue();
                 // stash ID for future reference
                 $vs_key = strlen($vs_id) > 0 ? $vs_id : md5($vn_row);
                 $va_mapping[$vs_key] = array('parent_id' => $vs_parent_id, 'element' => $vs_element, 'source' => $vs_mode == "RepeatMappings" ? null : $vs_source, 'options' => $va_options);
                 // allow mapping repetition
                 if ($vs_mode == 'RepeatMappings') {
                     if (strlen($vs_source) < 1) {
                         // ignore repitition rows without value
                         continue;
                     }
                     $va_new_items = array();
                     $va_mapping_items_to_repeat = explode(',', $vs_source);
                     foreach ($va_mapping_items_to_repeat as $vs_mapping_item_to_repeat) {
                         $vs_mapping_item_to_repeat = trim($vs_mapping_item_to_repeat);
                         if (!is_array($va_mapping[$vs_mapping_item_to_repeat])) {
                             $pa_errors[] = _t("Couldn't repeat mapping item %1", $vs_mapping_item_to_repeat);
                             continue;
                         }
                         // add item to repeat under current item
                         $va_new_items[$vs_key . "_:_" . $vs_mapping_item_to_repeat] = $va_mapping[$vs_mapping_item_to_repeat];
                         $va_new_items[$vs_key . "_:_" . $vs_mapping_item_to_repeat]['parent_id'] = $vs_key;
                         // Find children of item to repeat (and their children) and add them as well, preserving the hierarchy
                         // the code below banks on the fact that hierarchy children are always defined AFTER their parents
                         // in the mapping document.
                         $va_keys_to_lookup = array($vs_mapping_item_to_repeat);
                         foreach ($va_mapping as $vs_item_key => $va_item) {
                             if (in_array($va_item['parent_id'], $va_keys_to_lookup)) {
                                 $va_keys_to_lookup[] = $vs_item_key;
                                 $va_new_items[$vs_key . "_:_" . $vs_item_key] = $va_item;
                                 $va_new_items[$vs_key . "_:_" . $vs_item_key]['parent_id'] = $vs_key . ($va_item['parent_id'] ? "_:_" . $va_item['parent_id'] : "");
                             }
                         }
                     }
                     $va_mapping = $va_mapping + $va_new_items;
                 }
                 break;
             case 'Setting':
                 $o_setting_name = $o_sheet->getCellByColumnAndRow(1, $o_row->getRowIndex());
                 $o_setting_value = $o_sheet->getCellByColumnAndRow(2, $o_row->getRowIndex());
                 $va_settings[(string) $o_setting_name->getValue()] = (string) $o_setting_value->getValue();
                 break;
             default:
                 // if 1st column is empty, skip
                 continue 2;
                 break;
         }
     }
     // try to extract replacements from 2nd sheet in file
     // PHPExcel will throw an exception if there's no such sheet
     try {
         $o_sheet = $o_excel->getSheet(1);
         $vn_row = 0;
         foreach ($o_sheet->getRowIterator() as $o_row) {
             if ($vn_row == 0) {
                 // skip first row (headers)
                 $vn_row++;
                 continue;
             }
             $vn_row_num = $o_row->getRowIndex();
             $o_cell = $o_sheet->getCellByColumnAndRow(0, $vn_row_num);
             $vs_mapping_num = trim((string) $o_cell->getValue());
             if (strlen($vs_mapping_num) < 1) {
                 continue;
             }
             $o_search = $o_sheet->getCellByColumnAndRow(1, $o_row->getRowIndex());
             $o_replace = $o_sheet->getCellByColumnAndRow(2, $o_row->getRowIndex());
             if (!isset($va_mapping[$vs_mapping_num])) {
                 $pa_errors[] = _t("Warning: Replacement sheet references invalid mapping number '%1'. Ignoring row.", $vs_mapping_num);
                 continue;
             }
             $vs_search = (string) $o_search->getValue();
             $vs_replace = (string) $o_replace->getValue();
             if (!$vs_search) {
                 $pa_errors[] = _t("Warning: Search must be set for each row in the replacement sheet. Ignoring row for mapping '%1'", $vs_mapping_num);
                 continue;
             }
             // look for replacements
             foreach ($va_mapping as $vs_k => &$va_v) {
                 if (preg_match("!\\_\\:\\_" . $vs_mapping_num . "\$!", $vs_k)) {
                     $va_v['options']['original_values'][] = $vs_search;
                     $va_v['options']['replacement_values'][] = $vs_replace;
                 }
             }
             $va_mapping[$vs_mapping_num]['options']['original_values'][] = $vs_search;
             $va_mapping[$vs_mapping_num]['options']['replacement_values'][] = $vs_replace;
             $vn_row++;
         }
     } catch (PHPExcel_Exception $e) {
         // noop, because we don't care: mappings without replacements are still valid
     }
     // Do checks on mapping
     if (!$va_settings['code']) {
         $pa_errors[] = _t("Error: You must set a code for your mapping!");
         return;
     }
     $o_dm = Datamodel::load();
     if (!($t_instance = $o_dm->getInstanceByTableName($va_settings['table']))) {
         $pa_errors[] = _t("Error: Mapping target table %1 is invalid!", $va_settings['table']);
         return;
     }
     if (!$va_settings['name']) {
         $va_settings['name'] = $va_settings['code'];
     }
     $t_exporter = new ca_data_exporters();
     $t_exporter->setMode(ACCESS_WRITE);
     // Remove any existing mapping with this code
     if ($t_exporter->load(array('exporter_code' => $va_settings['code']))) {
         $t_exporter->delete(true, array('hard' => true));
         if ($t_exporter->numErrors()) {
             $pa_errors[] = _t("Could not delete existing mapping for %1: %2", $va_settings['code'], join("; ", $t_exporter->getErrors()));
             return;
         }
     }
     // Create new mapping
     $t_exporter->set('exporter_code', $va_settings['code']);
     $t_exporter->set('table_num', $t_instance->tableNum());
     $vs_name = $va_settings['name'];
     unset($va_settings['code']);
     unset($va_settings['table']);
     unset($va_settings['name']);
     foreach ($va_settings as $vs_k => $vs_v) {
         $t_exporter->setSetting($vs_k, $vs_v);
     }
     $t_exporter->insert();
     if ($t_exporter->numErrors()) {
         $pa_errors[] = _t("Error creating exporter: %1", join("; ", $t_exporter->getErrors()));
         return;
     }
     $t_exporter->addLabel(array('name' => $vs_name), $vn_locale_id, null, true);
     if ($t_exporter->numErrors()) {
         $pa_errors[] = _t("Error creating exporter name: %1", join("; ", $t_exporter->getErrors()));
         return;
     }
     $va_id_map = array();
     foreach ($va_mapping as $vs_mapping_id => $va_info) {
         $va_item_settings = array();
         if (is_array($va_info['options'])) {
             foreach ($va_info['options'] as $vs_k => $vs_v) {
                 switch ($vs_k) {
                     case 'replacement_values':
                     case 'original_values':
                         if (sizeof($vs_v) > 0) {
                             $va_item_settings[$vs_k] = join("\n", $vs_v);
                         }
                         break;
                     default:
                         $va_item_settings[$vs_k] = $vs_v;
                         break;
                 }
             }
         }
         $vn_parent_id = null;
         if ($va_info['parent_id']) {
             $vn_parent_id = $va_id_map[$va_info['parent_id']];
         }
         $t_item = $t_exporter->addItem($vn_parent_id, $va_info['element'], $va_info['source'], $va_item_settings);
         if ($t_exporter->numErrors()) {
             $pa_errors[] = _t("Error adding item to exporter: %1", join("; ", $t_exporter->getErrors()));
             return;
         }
         $va_id_map[$vs_mapping_id] = $t_item->getPrimaryKey();
     }
     $va_mapping_errors = ca_data_exporters::checkMapping($t_exporter->get('exporter_code'));
     if (is_array($va_mapping_errors) && sizeof($va_mapping_errors) > 0) {
         $pa_errors = array_merge($pa_errors, $va_mapping_errors);
         return false;
     }
     return $t_exporter;
 }
 /**
  * Prepare export generated by ExportData action
  */
 public function SetupBatchExport()
 {
     $o_conf = Configuration::load();
     $o_session = $this->getRequest()->getSession();
     if (!($vn_exporter_id = $o_session->getVar('exporter_id'))) {
         $this->getResponse()->setRedirect($this->getRequest()->config->get('error_display_url') . '/n/3420?r=' . urlencode($this->getRequest()->getFullUrlPath()));
         return;
     }
     $t_exporter = new ca_data_exporters($vn_exporter_id);
     if (!$t_exporter->getPrimaryKey()) {
         $this->getResponse()->setRedirect($this->getRequest()->config->get('error_display_url') . '/n/3420?r=' . urlencode($this->getRequest()->getFullUrlPath()));
         return;
     }
     $t_subject = $t_exporter->getAppDatamodel()->getInstanceByTableNum($t_exporter->get('table_num'), true);
     // alternate destinations
     $va_alt_dest = $o_conf->getAssoc('exporter_alternate_destinations');
     $this->getView()->setVar('exporter_alternate_destinations', $va_alt_dest);
     // filename set via request wins
     $vs_filename = $this->getRequest()->getParameter('file_name', pString);
     // otherwise get from config file
     if (!$vs_filename) {
         if ($vs_filename = $o_conf->get($t_subject->tableName() . "_batch_export_filename")) {
             // config setting comes without file extension
             $vs_filename = $vs_filename . '.' . $t_exporter->getFileExtension();
         }
     }
     // still no filename? -> go for hardcoded default
     if (!$vs_filename) {
         $vs_filename = 'batch_export.' . $t_exporter->getFileExtension();
     }
     // pass to view
     $this->getView()->setVar('file_name', $vs_filename);
     $this->render('export/export_destination_html.php');
 }