From a56a5fc2288911ee1800be8859768c6b6c36f11c Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 2 Jul 2016 17:33:57 +0200 Subject: [PATCH] New code for import routine. --- app/Http/Controllers/ImportController.php | 70 ++++++++- app/Import/Importer/CsvImporter.php | 137 +++++++++++++----- app/Import/Importer/ImporterInterface.php | 17 ++- config/csv.php | 10 +- resources/lang/en_US/csv.php | 85 +++++++---- resources/lang/en_US/firefly.php | 16 +- resources/lang/en_US/form.php | 2 +- resources/views/import/csv/configure.twig | 24 ++- .../views/import/csv/{map.twig => roles.twig} | 29 ++-- resources/views/import/index.twig | 1 + resources/views/index.twig | 2 +- 11 files changed, 264 insertions(+), 129 deletions(-) rename resources/views/import/csv/{map.twig => roles.twig} (65%) diff --git a/app/Http/Controllers/ImportController.php b/app/Http/Controllers/ImportController.php index 3416a5a6fd..c3c1763bc9 100644 --- a/app/Http/Controllers/ImportController.php +++ b/app/Http/Controllers/ImportController.php @@ -10,8 +10,10 @@ use FireflyIII\Import\Importer\ImporterInterface; use FireflyIII\Models\ImportJob; use FireflyIII\Repositories\ImportJob\ImportJobRepositoryInterface; use Illuminate\Http\Request; +use Log; use SplFileObject; use Storage; +use Symfony\Component\HttpFoundation\File\UploadedFile; use View; /** @@ -50,9 +52,11 @@ class ImportController extends Controller // actual code $importer = $this->makeImporter($job); $importer->configure(); - $data = $importer->getConfigurationData(); + $data = $importer->getConfigurationData(); + $subTitle = trans('firefly.configure_import'); + $subTitleIcon = 'fa-wrench'; - return view('import.' . $job->file_type . '.configure', compact('data', 'job')); + return view('import.' . $job->file_type . '.configure', compact('data', 'job', 'subTitle', 'subTitleIcon')); } @@ -106,6 +110,28 @@ class ImportController extends Controller return redirect(route('import.settings', $job->key)); } + /** + * This step 6. Depending on the importer, this will process the + * settings given and store them. + * + * @param Request $request + * @param ImportJob $job + * + * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector + * @throws FireflyException + */ + public function postSettings(Request $request, ImportJob $job) + { + if (!$this->jobInCorrectStep($job, 'store-settings')) { + return $this->redirectToCorrectStep($job); + } + $importer = $this->makeImporter($job); + $importer->storeSettings($request); + + // return redirect to settings (for more settings perhaps) + return redirect(route('import.settings', [$job->key])); + } + /** * Step 5. Depending on the importer, this will show the user settings to * fill in. @@ -120,18 +146,20 @@ class ImportController extends Controller if (!$this->jobInCorrectStep($job, 'settings')) { return $this->redirectToCorrectStep($job); } - $importer = $this->makeImporter($job); + $importer = $this->makeImporter($job); + $subTitle = trans('firefy.settings_for_import'); + $subTitleIcon = 'fa-wrench'; // now show settings screen to user. if ($importer->requireUserSettings()) { $data = $importer->getDataForSettings(); $view = $importer->getViewForSettings(); - return view($view, compact('data', 'job')); + return view($view, compact('data', 'job', 'subTitle', 'subTitleIcon')); } // if no more settings, save job and continue to process thing. - + echo 'now in settings (done)'; exit; @@ -155,8 +183,11 @@ class ImportController extends Controller public function upload(ImportUploadRequest $request, ImportJobRepositoryInterface $repository) { // create import job: - $type = $request->get('import_file_type'); - $job = $repository->create($type); + $type = $request->get('import_file_type'); + $job = $repository->create($type); + Log::debug('Created new job', ['key' => $job->key, 'id' => $job->id]); + + /** @var UploadedFile $upload */ $upload = $request->files->get('import_file'); $newName = $job->key . '.upload'; $uploaded = new SplFileObject($upload->getRealPath()); @@ -165,6 +196,30 @@ class ImportController extends Controller $disk = Storage::disk('upload'); $disk->put($newName, $contentEncrypted); + Log::debug('Uploaded file', ['name' => $upload->getClientOriginalName(), 'size' => $upload->getSize(), 'mime' => $upload->getClientMimeType()]); + + // store configuration file's content into the job's configuration + // thing. + // otherwise, leave it empty. + if ($request->files->has('configuration_file')) { + /** @var UploadedFile $configFile */ + $configFile = $request->files->get('configuration_file'); + Log::debug( + 'Uploaded configuration file', + ['name' => $configFile->getClientOriginalName(), 'size' => $configFile->getSize(), 'mime' => $configFile->getClientMimeType()] + ); + + $configFileObject = new SplFileObject($configFile->getRealPath()); + $configRaw = $configFileObject->fread($configFileObject->getSize()); + $configuration = json_decode($configRaw, true); + + if (!is_null($configuration) && is_array($configuration)) { + Log::debug('Found configuration', $configuration); + $job->configuration = $configuration; + $job->save(); + } + } + return redirect(route('import.configure', [$job->key])); } @@ -183,6 +238,7 @@ class ImportController extends Controller return $job->status === 'import_status_never_started'; break; case 'settings': + case 'store-settings': return $job->status === 'import_configuration_saved'; break; } diff --git a/app/Import/Importer/CsvImporter.php b/app/Import/Importer/CsvImporter.php index ece4c9e527..7a592442a4 100644 --- a/app/Import/Importer/CsvImporter.php +++ b/app/Import/Importer/CsvImporter.php @@ -14,10 +14,11 @@ namespace FireflyIII\Import\Importer; use ExpandedForm; use FireflyIII\Crud\Account\AccountCrud; -use FireflyIII\Import\Role\Map; use FireflyIII\Models\AccountType; use FireflyIII\Models\ImportJob; +use Illuminate\Http\Request; use League\Csv\Reader; +use Log; use Symfony\Component\HttpFoundation\FileBag; /** @@ -32,11 +33,38 @@ class CsvImporter implements ImporterInterface public $job; /** + * Create initial (empty) configuration array. + * + * + * * @return bool */ public function configure(): bool { + if (is_null($this->job->configuration) || (is_array($this->job->configuration) && count($this->job->configuration) === 0)) { + Log::debug('No config detected, will create empty one.'); + + $config = [ + 'has-headers' => false, // assume + 'date-format' => 'Ymd', // assume + 'delimiter' => ',', // assume + 'import-account' => 0, // none, + 'specifics' => [], // none + 'column-count' => 0, // unknown + 'column-roles' => [], // unknown + 'column-do-mapping' => [], // not yet set which columns must be mapped + 'column-roles-complete' => false, // not yet configured roles for columns + 'column-mapping-config' => [], // no mapping made yet. + 'column-mapping-complete' => false, // so mapping is not complete. + ]; + $this->job->configuration = $config; + $this->job->save(); + + return true; + } + // need to do nothing, for now. + Log::debug('Detected config in upload, will use that one. ', $this->job->configuration); return true; } @@ -89,15 +117,17 @@ class CsvImporter implements ImporterInterface 'columnCount' => 0, ]; - if (!isset($config['columns'])) { + if ($this->doColumnRoles()) { - // show user column configuration. + // show user column role configuration. $content = $this->job->uploadFileContents(); // create CSV reader. $reader = Reader::createFromString($content); - $start = $config['has_headers'] ? 1 : 0; + $start = $config['has-headers'] ? 1 : 0; $end = $start + self::EXAMPLE_ROWS; // first X rows + + // collect example data in $data['columns'] while ($start < $end) { $row = $reader->fetchOne($start); foreach ($row as $index => $value) { @@ -110,20 +140,28 @@ class CsvImporter implements ImporterInterface $data['columnCount'] = count($row); } - // make unique + // make unique example data foreach ($data['columns'] as $index => $values) { $data['columns'][$index] = array_unique($values); } - // TODO preset roles from config + $data['set_roles'] = []; // collect possible column roles: $data['available_roles'] = []; foreach (array_keys(config('csv.import_roles')) as $role) { - $data['available_roles'][$role] = trans('csv.csv_column_'.$role); + $data['available_roles'][$role] = trans('csv.column_' . $role); } + $config['column-count'] = $data['columnCount']; + $this->job->configuration = $config; + $this->job->save(); + return $data; } + + + echo 'no settings to do.'; + exit; } @@ -135,18 +173,10 @@ class CsvImporter implements ImporterInterface */ public function getViewForSettings(): string { - return 'import.csv.map'; - } - - /** - * Returns a Map thing used to allow the user to - * define roles for each entry. - * - * @return Map - */ - public function prepareRoles(): Map - { - return 'do not work'; + if ($this->doColumnRoles()) { + return 'import.csv.roles'; + } + echo 'no view for settings'; exit; } @@ -174,33 +204,25 @@ class CsvImporter implements ImporterInterface */ public function saveImportConfiguration(array $data, FileBag $files): bool { - /* - * TODO file upload is ignored for now. - */ - /** @var AccountCrud $repository */ - $repository = app(AccountCrud::class); - $account = $repository->find(intval($data['csv_import_account'])); - $hasHeaders = isset($data['has_headers']) && intval($data['has_headers']) === 1 ? true : false; - $configuration = [ - 'has_headers' => $hasHeaders, - 'date_format' => $data['date_format'], - 'csv_delimiter' => $data['csv_delimiter'], - 'csv_import_account' => 0, - 'specifics' => [], - - ]; + $repository = app(AccountCrud::class); + $account = $repository->find(intval($data['csv_import_account'])); + $hasHeaders = isset($data['has_headers']) && intval($data['has_headers']) === 1 ? true : false; + $config = $this->job->configuration; + $config['has-headers'] = $hasHeaders; + $config['date-format'] = $data['date_format']; + $config['delimiter'] = $data['csv_delimiter']; if (!is_null($account->id)) { - $configuration['csv_import_account'] = $account->id; + $config['import-account'] = $account->id; } // loop specifics. - if (is_array($data['specifics'])) { + if (isset($data['specifics']) && is_array($data['specifics'])) { foreach ($data['specifics'] as $name => $enabled) { - $configuration['specifics'][] = $name; + $config['specifics'][$name] = 1; } } - $this->job->configuration = $configuration; + $this->job->configuration = $config; $this->job->save(); return true; @@ -215,4 +237,43 @@ class CsvImporter implements ImporterInterface { $this->job = $job; } + + /** + * Store the settings filled in by the user, if applicable. + * + * @param Request $request + * + */ + public function storeSettings(Request $request) + { + $config = $this->job->configuration; + $count = $config['column-count']; + $all = $request->all(); + $roleSet = 0; + for ($i = 0; $i < $count; $i++) { + $selectedRole = $all['role'][$i] ?? '_ignore'; + $doMapping = isset($all['map'][$i]) && $all['map'][$i] == '1' ? true : false; + if ($selectedRole == '_ignore' && $doMapping === true) { + $doMapping = false; // cannot map ignored columns. + } + if ($selectedRole != '_ignore') { + $roleSet++; + } + $config['column-roles'][$i] = $selectedRole; + $config['column-do-mapping'][$i] = $doMapping; + } + if ($roleSet > 0) { + $config['column-roles-complete'] = true; + $this->job->configuration = $config; + $this->job->save(); + } + } + + /** + * @return bool + */ + private function doColumnRoles(): bool + { + return $this->job->configuration['column-roles-complete'] === false; + } } \ No newline at end of file diff --git a/app/Import/Importer/ImporterInterface.php b/app/Import/Importer/ImporterInterface.php index 45135fc26b..6e332da8fe 100644 --- a/app/Import/Importer/ImporterInterface.php +++ b/app/Import/Importer/ImporterInterface.php @@ -13,6 +13,7 @@ namespace FireflyIII\Import\Importer; use FireflyIII\Import\Role\Map; use FireflyIII\Models\ImportJob; +use Illuminate\Http\Request; use Symfony\Component\HttpFoundation\FileBag; /** @@ -44,6 +45,14 @@ interface ImporterInterface */ public function getDataForSettings(): array; + /** + * Store the settings filled in by the user, if applicable. + * + * @param Request $request + * + */ + public function storeSettings(Request $request); + /** * This method returns the name of the view that will be shown to the user to further configure * the import job. @@ -52,14 +61,6 @@ interface ImporterInterface */ public function getViewForSettings(): string; - /** - * Returns a Map thing used to allow the user to - * define roles for each entry. - * - * @return Map - */ - public function prepareRoles(): Map; - /** * This method returns whether or not the user must configure this import * job further. diff --git a/config/csv.php b/config/csv.php index 3622624496..e235133f32 100644 --- a/config/csv.php +++ b/config/csv.php @@ -171,11 +171,11 @@ return [ 'converter' => 'Amount', 'field' => 'amount', ], - 'amount-comma-separated' => [ - 'mappable' => false, - 'converter' => 'AmountComma', - 'field' => 'amount', - ], +// 'amount-comma-separated' => [ +// 'mappable' => false, +// 'converter' => 'AmountComma', +// 'field' => 'amount', +// ], 'sepa-ct-id' => [ 'mappable' => false, 'converter' => 'Description', diff --git a/resources/lang/en_US/csv.php b/resources/lang/en_US/csv.php index f9878e6938..c27e945dce 100644 --- a/resources/lang/en_US/csv.php +++ b/resources/lang/en_US/csv.php @@ -10,35 +10,58 @@ declare(strict_types = 1); return [ - 'csv_column__ignore' => '(ignore this column)', - 'csv_column_account-iban' => 'Asset account (IBAN)', - 'csv_column_account-id' => 'Asset account ID (matching Firefly)', - 'csv_column_account-name' => 'Asset account (name)', - 'csv_column_amount' => 'Amount', - 'csv_column_amount-comma-separated' => 'Amount (comma as decimal separator)', - 'csv_column_bill-id' => 'Bill ID (matching Firefly)', - 'csv_column_bill-name' => 'Bill name', - 'csv_column_budget-id' => 'Budget ID (matching Firefly)', - 'csv_column_budget-name' => 'Budget name', - 'csv_column_category-id' => 'Category ID (matching Firefly)', - 'csv_column_category-name' => 'Category name', - 'csv_column_currency-code' => 'Currency code (ISO 4217)', - 'csv_column_currency-id' => 'Currency ID (matching Firefly)', - 'csv_column_currency-name' => 'Currency name (matching Firefly)', - 'csv_column_currency-symbol' => 'Currency symbol (matching Firefly)', - 'csv_column_date-rent' => 'Rent calculation date', - 'csv_column_date-transaction' => 'Date', - 'csv_column_description' => 'Description', - 'csv_column_opposing-iban' => 'Opposing account (IBAN)', - 'csv_column_opposing-id' => 'Opposing account ID (matching Firefly)', - 'csv_column_opposing-name' => 'Opposing account (name)', - 'csv_column_rabo-debet-credit' => 'Rabobank specific debet/credit indicator', - 'csv_column_ing-debet-credit' => 'ING specific debet/credit indicator', - 'csv_column_sepa-ct-id' => 'SEPA Credit Transfer end-to-end ID', - 'csv_column_sepa-ct-op' => 'SEPA Credit Transfer opposing account', - 'csv_column_sepa-db' => 'SEPA Direct Debet', - 'csv_column_tags-comma' => 'Tags (comma separated)', - 'csv_column_tags-space' => 'Tags (space separated)', - 'csv_column_account-number' => 'Asset account (account number)', - 'csv_column_opposing-number' => 'Opposing account (account number)', + + 'import_configure_title' => 'Configure your import', + 'import_configure_intro' => 'There are some options for your CSV import.', + 'import_configure_form' => 'Form', + 'header_help' => 'Check this if the first row of your CSV file are the column titles', + 'date_help' => 'Date time format in your CSV. Follow the format like this page indicates. The default value will parse dates that look like this: :dateExample.', + 'delimiter_help' => 'Choose the field delimiter that is used in your input file. If not sure, comma is the safest option.', + 'config_file_help' => 'Select your CSV import configuration here. If you do not know what this is, ignore it. It will be explained later.', + 'import_account_help' => 'If your CSV file does NOT contain information about your asset account(s), use this dropdown to select to which account the transactions in the CSV belong to.', + 'upload_not_writeable' => 'The grey box contains a file path. It should be writeable. Please make sure it is.', + + // roles + 'column_roles_title' => 'Define column roles', + 'column_roles_text' => 'Each column contains some data. What data?', + 'column_roles_table' => 'Table', + 'column_name' => 'Name of column', + 'column_example' => 'Column example data', + 'column_role' => 'Column data meaning', + 'do_map_value' => 'Map these values', + 'column' => 'Column', + 'no_example_data' => 'No example data available', + 'store_column_roles' => 'Continue import', + + 'column__ignore' => '(ignore this column)', + 'column_account-iban' => 'Asset account (IBAN)', + 'column_account-id' => 'Asset account ID (matching Firefly)', + 'column_account-name' => 'Asset account (name)', + 'column_amount' => 'Amount', + 'column_amount-comma-separated' => 'Amount (comma as decimal separator)', + 'column_bill-id' => 'Bill ID (matching Firefly)', + 'column_bill-name' => 'Bill name', + 'column_budget-id' => 'Budget ID (matching Firefly)', + 'column_budget-name' => 'Budget name', + 'column_category-id' => 'Category ID (matching Firefly)', + 'column_category-name' => 'Category name', + 'column_currency-code' => 'Currency code (ISO 4217)', + 'column_currency-id' => 'Currency ID (matching Firefly)', + 'column_currency-name' => 'Currency name (matching Firefly)', + 'column_currency-symbol' => 'Currency symbol (matching Firefly)', + 'column_date-rent' => 'Rent calculation date', + 'column_date-transaction' => 'Date', + 'column_description' => 'Description', + 'column_opposing-iban' => 'Opposing account (IBAN)', + 'column_opposing-id' => 'Opposing account ID (matching Firefly)', + 'column_opposing-name' => 'Opposing account (name)', + 'column_rabo-debet-credit' => 'Rabobank specific debet/credit indicator', + 'column_ing-debet-credit' => 'ING specific debet/credit indicator', + 'column_sepa-ct-id' => 'SEPA Credit Transfer end-to-end ID', + 'column_sepa-ct-op' => 'SEPA Credit Transfer opposing account', + 'column_sepa-db' => 'SEPA Direct Debet', + 'column_tags-comma' => 'Tags (comma separated)', + 'column_tags-space' => 'Tags (space separated)', + 'column_account-number' => 'Asset account (account number)', + 'column_opposing-number' => 'Opposing account (account number)', ]; \ No newline at end of file diff --git a/resources/lang/en_US/firefly.php b/resources/lang/en_US/firefly.php index ede1f038f6..077c75cb33 100644 --- a/resources/lang/en_US/firefly.php +++ b/resources/lang/en_US/firefly.php @@ -749,18 +749,12 @@ return [ 'split_this_transfer' => 'Split this transfer', // import + 'configuration_file_help' => 'If you have previously imported data into Firefly III, you may have a configuration file, which will pre-set configuration values for you.', + 'import_data_index' => 'Index', 'import_file_type_csv' => 'CSV (comma separated values)', 'import_file_type_help' => 'Select the type of file you will upload', 'import_start' => 'Start the import', - 'import_csv_configure_title' => 'Configure your import', - 'import_csv_configure_intro' => 'There are some options for your CSV import.', - 'import_csv_configure_form' => 'Form', - 'csv_header_help' => 'Check this if the first row of your CSV file are the column titles', - 'csv_date_help' => 'Date time format in your CSV. Follow the format like this page indicates. The default value will parse dates that look like this: :dateExample.', - 'csv_delimiter_help' => 'Choose the field delimiter that is used in your input file. If not sure, comma is the safest option.', - 'csv_csv_config_file_help' => 'Select your CSV import configuration here. If you do not know what this is, ignore it. It will be explained later.', - 'csv_import_account_help' => 'If your CSV file does NOT contain information about your asset account(s), use this dropdown to select to which account the transactions in the CSV belong to.', - 'csv_upload_not_writeable' => 'The grey box contains a file path. It should be writeable. Please make sure it is.', - - + 'configure_import' => 'Further configure your import', + 'import_finish_configuration' => 'Finish configuration', + 'settings_for_import' => 'Settings', ]; diff --git a/resources/lang/en_US/form.php b/resources/lang/en_US/form.php index c2bf0cb5e9..0caec98f1c 100644 --- a/resources/lang/en_US/form.php +++ b/resources/lang/en_US/form.php @@ -133,6 +133,7 @@ return [ // import 'import_file' => 'Import file', + 'configuration_file' => 'Configuration file', 'import_file_type' => 'Import file type', 'csv_comma' => 'A comma (,)', 'csv_semicolon' => 'A semicolon (;)', @@ -142,5 +143,4 @@ return [ 'csv_config' => 'CSV import configuration', - ]; diff --git a/resources/views/import/csv/configure.twig b/resources/views/import/csv/configure.twig index 64adea984a..0cbab054f3 100644 --- a/resources/views/import/csv/configure.twig +++ b/resources/views/import/csv/configure.twig @@ -10,11 +10,11 @@
-

{{ 'import_csv_configure_title'|_ }}

+

{{ trans('csv.import_configure_title') }}

- {{ 'import_csv_configure_intro'|_ }} + {{ trans('csv.import_configure_intro') }}

@@ -29,17 +29,14 @@
-

{{ 'import_csv_configure_form'|_ }}

+

{{ trans('csv.import_configure_form') }}

- {{ ExpandedForm.checkbox('has_headers',1,null,{helpText: 'csv_header_help'|_}) }} - {{ ExpandedForm.text('date_format','Ymd',{helpText: trans('firefly.csv_date_help', {dateExample: phpdate('Ymd')}) }) }} - {{ ExpandedForm.select('csv_delimiter', data.delimiters, 0, {helpText: 'csv_delimiter_help'|_} ) }} - - {{ ExpandedForm.file('csv_config',{helpText: 'csv_csv_config_file_help'|_}) }} - - {{ ExpandedForm.select('csv_import_account', data.accounts, 0, {helpText: 'csv_import_account_help'|_} ) }} + {{ ExpandedForm.checkbox('has_headers',1,job.configuration['has-headers'],{helpText: trans('csv.header_help')}) }} + {{ ExpandedForm.text('date_format',job.configuration['date-format'],{helpText: trans('csv.date_help', {dateExample: phpdate('Ymd')}) }) }} + {{ ExpandedForm.select('csv_delimiter', data.delimiters, job.configuration['delimiter'], {helpText: trans('csv.delimiter_help') } ) }} + {{ ExpandedForm.select('csv_import_account', data.accounts, 0, {helpText: trans('csv.import_account_help')} ) }} {% for type, specific in data.specifics %}
@@ -49,7 +46,8 @@
@@ -66,7 +64,7 @@
{{ data.upload_path }}

- {{ 'csv_upload_not_writeable'|_ }} + {{ trans('csv.upload_not_writeable') }}

@@ -82,7 +80,7 @@
diff --git a/resources/views/import/csv/map.twig b/resources/views/import/csv/roles.twig similarity index 65% rename from resources/views/import/csv/map.twig rename to resources/views/import/csv/roles.twig index fb141d4432..c1f317cbbf 100644 --- a/resources/views/import/csv/map.twig +++ b/resources/views/import/csv/roles.twig @@ -10,10 +10,10 @@
-

{{ 'csv_column_roles_title'|_ }}

+

{{ trans('csv.column_roles_title') }}

-

{{ 'csv_column_roles_text'|_ }}

+

{{ trans('csv.column_roles_text') }}

@@ -21,30 +21,32 @@
+
-

{{ 'csv_column_roles_table'|_ }}

+

{{ trans('csv.column_roles_table') }}

- - - - + + + + - {% for i in 0..data.columnCount %} + {% for i in 0..(data.columnCount-1) %} + - + @@ -74,9 +76,8 @@
- {{ 'csv_go_back'|_ }}
diff --git a/resources/views/import/index.twig b/resources/views/import/index.twig index 8449e5a93f..b69d953026 100644 --- a/resources/views/import/index.twig +++ b/resources/views/import/index.twig @@ -26,6 +26,7 @@
{{ ExpandedForm.file('import_file', {helpText: 'import_file_help'|_}) }} + {{ ExpandedForm.file('configuration_file', {helpText: 'configuration_file_help'|_}) }} {{ ExpandedForm.select('import_file_type', importFileTypes, defaultImportType, {'helpText' : 'import_file_type_help'|_}) }} diff --git a/resources/views/index.twig b/resources/views/index.twig index 4363bbf924..64a8e1d873 100644 --- a/resources/views/index.twig +++ b/resources/views/index.twig @@ -59,7 +59,7 @@ {% for data in transactions %}
-

{{ data[1].name }}

+

{{ data[1].name }}

{{ 'csv_column_name'|_ }}{{ 'csv_column_example'|_ }}{{ 'csv_column_role'|_ }}{{ 'csv_do_map_value'|_ }}{{ trans('csv.column_name') }}{{ trans('csv.column_example') }}{{ trans('csv.column_role') }}{{ trans('csv.do_map_value') }}
Column #{{ loop.index }}{{ trans('csv.column') }} #{{ loop.index }} {% if data.columns[i]|length == 0 %} - No example data available + {{ trans('csv.no_example_data') }} {% else %} {% for example in data.columns[i] %} {{ example }}
@@ -52,10 +54,10 @@ {% endif %}
- {{ Form.select(('role['~index~']'), data.available_roles,data.set_roles[index],{class: 'form-control'}) }} + {{ Form.select(('role['~loop.index0~']'), data.available_roles,data.set_roles[index],{class: 'form-control'}) }} - {# Form.checkbox(('map['~index~']'),1,map[index]) #} + {{ Form.checkbox(('map['~loop.index0~']'),1,map[index]) }}