From e52bb077cbc2068a69475d616fd6562ca843bb15 Mon Sep 17 00:00:00 2001 From: totoro Date: Fri, 12 May 2017 00:25:23 +0900 Subject: [PATCH] add import --- controller/hk_controller.anubis | 1 + controller/hk_rows_import.anubis | 314 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ model/utils.anubis | 79 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ view/view_apps_renderer.anubis | 8 +++++++- view/view_rows_import.anubis | 214 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ view/view_table_renderer.anubis | 7 ++----- view/web_action.anubis | 6 +++++- 7 files changed, 622 insertions(+), 7 deletions(-) create mode 100644 controller/hk_rows_import.anubis create mode 100644 model/utils.anubis create mode 100644 view/view_rows_import.anubis diff --git a/controller/hk_controller.anubis b/controller/hk_controller.anubis index 577d24f..d370787 100644 --- a/controller/hk_controller.anubis +++ b/controller/hk_controller.anubis @@ -8,3 +8,4 @@ transmit hayamiki_lib/controller/hk_delete.anubis + transmit kayamiki_lib/controller/hk_rows_import.anubis diff --git a/controller/hk_rows_import.anubis b/controller/hk_rows_import.anubis new file mode 100644 index 0000000..3479be2 --- /dev/null +++ b/controller/hk_rows_import.anubis @@ -0,0 +1,314 @@ +/* + * Created by PyramIDE. + * User: フランスのトトロ aka (David RENÉ) + * Date: 09/05/2017 + * Time: 15:27 + * © Calexium + */ + +read data_base/sqlite.anubis +read data_base/read_csv.anubis +read tools/function.anubis + +read hayamiki_lib/model/types/hk_database.anubis +read calexium_lib/web/CXM_web_arg_utils.anubis +read calexium_lib/web/CXM_web_session.anubis +read calexium_lib/database/csv_import_status.anubis +read lexical_analysis/fast_lexer_4.anubis + + +define Maybe(SQLite3Bind) + bind_column_value + ( + String col, + String value + ) = + if col = "" then + failure + else + success(bind_String(":"+col, value)). + +define List(Int) +/* return a list of column index to get from CSV + */ + get_csv_columns + ( + List((Int, String)) columns, + List(Int) so_far + )= + map(((Int, String) col) |-> since col is (idx, _), idx, columns) +. + +define String + dump_csv_db_col + ( + List((Int, String)) columns, + String so_far + )= + if columns is + { + [] then so_far, + [h . t] then + if h is (csv_col, db_col) then + dump_csv_db_col(t, so_far + "csv_col : "+csv_col+" db col name : "+db_col+"\r\n") + }. + +define List(String) + get_db_columns + ( + List((Int, String)) columns, + List(String) so_far + )= + if columns is + { + [] then reverse(so_far), + [h . t] then + if h is (_, db_col) then + get_db_columns(t, [db_col . so_far]) + }. + + +define List((Int csv_col, String db_col)) + _get_columns_list + ( + List(Web_arg) lwa, + Int max_col, //maximum columns in source side + Int index, //current checking idx + List((Int, String)) so_far + ) = + if index >= max_col then + reverse(so_far) + else if get_String(lwa, "col_" + index) is + { + failure then _get_columns_list(lwa, max_col, index + 1, so_far), + success(db_col_name) then _get_columns_list(lwa, max_col, index + 1, [(index, db_col_name) . so_far]) + } +. + + +define List((Int csv_col, String db_col)) +/* get the list of source columns selected for importation process + */ + get_columns_list + ( + List(Web_arg) lwa + ) = + with max_col = get_Int(lwa, "hk_source_col", 0), + _get_columns_list(lwa, max_col, 0, []) +. + +define (Int, List(List(String))) + extract_lines + ( + One -> ReadCsvResult cvs_line_reader, + Int max_count, //number of line to extract + List(String) columns, + Int offset, + List(List(String)) so_far + ) = + if max_count =< 0 then + (offset, so_far) + else + if cvs_line_reader(unique) is + { + end_of_input then (offset, so_far) + error(e) then + //TODO report that error in importation report + println("error ["+e+"]\n"); (offset, so_far), + ok(_offset, all_values) then + //if line_data is (line, all_values) then + extract_lines(cvs_line_reader, max_count - 1, columns, _offset, [all_values . so_far]) + } +. + +define Result(String, One) + insert_data_to_table + ( + SQLite3Stmt insert_data_stmt, + List(String) columns, + List(String) values + )= + if sqlite3_reset(insert_data_stmt) is + { + error(err) then println(db_error(err, "Error reseting insert data statement")); error("DB Error"), + ok then + if sqlite3_bind(insert_data_stmt, map2_select(bind_column_value, columns, values)) is + { + error(err) then println(db_error(err, "Error binding insert data statement"));error("DB Error"), + ok then + if sqlite3_step(insert_data_stmt) is + { + error(err) then println(db_error(err, "Error executing insert data statement")); error("DB Error"), + no_more_row then ok(unique), + row(_) then ok(unique) + } + } + }. + +define Maybe(One) + store_data + ( + SQLite3Stmt insert_data_stmt, + SQLite3Stmt update_data_stmt, +// SQLite3Stmt select_contact_id_opt_out_stmt, +// SQLite3Stmt select_stmt, +// SQLite3Stmt insert_list_contact_stmt, +// SQLite3Stmt count_stmt, + List(String) columns, //list of column's name + List(List(String)) rows //list of list of real data to store + ) = + if rows is + { + [] then success(unique), + [row . t] then + if insert_data_to_table(insert_data_stmt, columns, row) is + { + error(msg) then + println("data can't be added "+msg); + //TODO as Julien's idea, copy error line in new csv file + store_data(insert_data_stmt, update_data_stmt, columns, t), + + ok(_) then + store_data(insert_data_stmt, update_data_stmt, columns, t) + } + } +. + +define CSV_Import_Status + main_loop + ( + SQLite3DataBase db, +// Int import_id, */ + SQLite3Stmt insert_data_stmt, + SQLite3Stmt update_data_stmt, + + One -> ReadCsvResult csv_line_reader, + //One -> Int progression, + UTime start_time, + List(String) columns, + Int line_num, + Int _offset, + Int offset_base + ) = +// with prog = progression(unique), +// logDebug(debug_log, "Importing contacts into mailing in progress... " + abs_to_decimal(prog) + " Bytes"); +// logDebug(debug_log, "Importing contacts into mailing in progress... Lines:" + line_num + " "+ (offset_base + offset) + " Bytes read"); + //import_csv_set_status(db, list_id, csv_in_progress); + with new_time = unow, + since new_time - start_time is utime(seconds, _), +//! import_task_update_progress(db, import_id, seconds, line_num, offset_base + offset); + + //get 500 lines from cvs + since extract_lines(csv_line_reader, 500, columns, _offset, []) is (offset, so_far), + with extract_length = length(so_far), + println("Extracted lines : "+extract_length); + + if extract_length = 0 then + csv_finished + else + if db_do_transaction( + db, + (One _) |-> store_data(insert_data_stmt, update_data_stmt, columns, so_far), + success((SQLite3Error err) |-> println(db_error(err,"import_csv_to_db"))), + //success((SQLite3Error err) |-> logError("DB", db_error(err,"import_csv_to_db"))), + 60000, // max 60s + 100 // retry every 100 ms + ) is + { + error(_) then csv_db_error, + ok(_) then main_loop(db, insert_data_stmt, update_data_stmt, csv_line_reader, new_time, columns, line_num + extract_length, offset, offset_base) + } +. + +define CSV_Import_Status + import_csv_to_db_transaction + ( + SQLite3DataBase db, + String table_name, +// Import_Task import_task, + One -> ReadCsvResult csv_line_reader, + //(One) -> Int progression, + List(String) columns, //columns selected for importation in same order from CSV file + UTime start_time, + Int offset_base + )= + //since import_task is import_task(import_task_id, list_id, filename, file_size, csv_db_columns, separator, charset, amount_time, offset, current_line, lines_count, status, status_details, update_contact, opt_out_import), + with current_line = (Int)0, + with offset = (Int)0, +// if get_domain_id_from_mailing(db, list_id) is +// { +// failure then logError(debug_log, "domain_id not found in import_csv_to_mailing_list"); csv_error, +// success(domain_id) then + with filter_columns = map_select((String s) |-> if s = "" then failure else success(s), columns), + sql_columns = join(", ", filter_columns), + sql_values = join(", ", map((String s) |-> ":"+s, filter_columns)), + with sql_update_values = join(", ", map((String s) |-> s+"=:"+s, filter_columns)), + + if sqlite3_prepare(db, "INSERT INTO "+table_name+" ("+sql_columns+") VALUES ("+sql_values+");") is + { + error(err) then //logError(debug_log, db_error(err, "preparing insert contact statement for import_csv_to_mailing_list")); + println(db_error(err, "preparing insert data statement for import_csv_to_mailing_list")); + csv_db_error, + ok(insert_data_stmt) then + + if sqlite3_prepare(db, "UPDATE "+table_name+" SET " + sql_update_values + " WHERE id=:id;") is + { + error(err) then //logError(debug_log, db_error(err, "preparing update contact statement for import_csv_to_mailing_list")); + println(db_error(err, "preparing update data statement for import_csv_to_db_transaction")); + csv_db_error, + ok(update_data_stmt) then + /* run the main loop */ + main_loop(db, /*import_task_id, */ insert_data_stmt, update_data_stmt, csv_line_reader, start_time, columns, current_line, offset, offset_base) + } + } +// } +. + + +public define Result(String, String) + hkc_import_execute + ( + WEB_Session _session, + SQLite3DataBase db, + HK_Database hk_db + )= + //arguments list + //table_name + //file + //csv_sep + //col_x = 'target_col', where x is the column number in csv file and target_col is name of the column in the table + //with _T = make_translate_function(_session.language), + with lwa = _session.web_request.lwa, + with separator = get_String(lwa, "csv_sep", ";"), + with csv_db_columns = get_columns_list(lwa), + with columns = get_db_columns(csv_db_columns, []), + table_name = get_String(lwa, "table_name", ""), + with start_time = unow, + offset_base = (Int)0, + skip_first_line = get_Bool(lwa, "hk_remove_csv_header", true), + + file_name_full_path = get_String(_session.fields, "upl_file", ""), + println(dump_csv_db_col(csv_db_columns,"")); + + if file(file_name_full_path, read) is + { + failure then + error("Can't open CSV file '" + file_name_full_path + "'") + //import_task_set_status(db, import_id, csv_file_not_found), + success(f) then + //with file_size = file_size(f), + if make_lexing_stream("", f, 65536, 10) is + { + failure then error("csv_error Error while reading file"), + success(ls) then + with csv_line_reader = make_read_csv_line(ls, separator, get_csv_columns(csv_db_columns, [])), + (if skip_first_line then + forget(csv_line_reader(unique)) + else + unique); + with status = import_csv_to_db_transaction(db, table_name, csv_line_reader, columns, start_time, offset_base), + ok("imported") + + } + } +. diff --git a/model/utils.anubis b/model/utils.anubis new file mode 100644 index 0000000..a8fd0a2 --- /dev/null +++ b/model/utils.anubis @@ -0,0 +1,79 @@ +/* + * Created by PyramIDE. + * User: フランスのトトロ aka (David RENÉ) + * Date: 07/05/2017 + * Time: 00:39 + * © Calexium + */ + +read hayamiki_lib/model/types/hk_database.anubis +read hayamiki_lib/model/types/hk_app.anubis +read hayamiki_lib/model/types/hk_table.anubis +read hayamiki_lib/model/types/hk_model.anubis +read hayamiki_lib/model/types/hk_model_column.anubis + +define Maybe(HK_Table) + get_table + ( + String prefix, //the prefix is app name if exists else nothing + List(HK_Table) tables, + String table_name + )= + if tables is + { + [] then failure, //table not found + [h. t] then + since h is hk_table(name, _, _, _), + println("testing "+table_name+" "+prefix+name); + if table_name = prefix+name then + success(h) + else + get_table(prefix, t, table_name) + } +. + +define Maybe(HK_Table) + get_table + ( + List(HK_App) apps, + String table_name + )= + if apps is + { + [] then failure, + [app . t] then + since app is hk_app(app_name, _, _, tables), + with prefix = if length(app_name) > 0 then app_name+"_" else "", + if get_table(prefix, tables, table_name) is + { + failure then get_table(t, table_name), + success(table) then success(table) + } + } +. + +public define Maybe(HK_Table) + get_table + ( + HK_Database hk_db, + String table_name + )= + since hk_db is hk_database(_, apps), + get_table(apps, table_name) +. + +public define List((String, String)) + get_columns_name_and_TAG + ( + HK_Table table + )= + [ ("id","ID") . + map((HK_Model_Column column) |-> + since column is hk_column(name, type, attributes, help), + (name, to_upper(name)) + , + table.model.columns + ) + ] +. + diff --git a/view/view_apps_renderer.anubis b/view/view_apps_renderer.anubis index f0288d2..3100b9e 100644 --- a/view/view_apps_renderer.anubis +++ b/view/view_apps_renderer.anubis @@ -25,7 +25,7 @@ define List(HTML_Row(HTML_Off_Form)) (String) -> String _T, )= map_append((HK_App app) |-> since app is hk_app(app_name, icon, help, tables), - [ (HTML_Row(HTML_Off_Form))row([core_attrs([class("hk_app_title")])],cell([columns(5)],text(app_name)))]+ //APP name line + [ (HTML_Row(HTML_Off_Form))row([core_attrs([class("hk_app_title")])],cell([columns(6)],text(app_name)))]+ //APP name line //here map for each table map((HK_Table table) |-> since table is hk_table(_table_name, short_name, model, display), //construct the real name of the table (app + table_name) @@ -44,6 +44,12 @@ define List(HTML_Row(HTML_Off_Form)) text(_T("LIST")) ]) ), + cell( + sequence([ + partial(img_button("icons/16x16/table_import.png", hkc_import_dropzone, [("table_name",table_name)])), + text(_T("CSV_IMPORT")) + ]) + ), cell(text(_T("RECORDS_COUNT")+" ")), cell([right], text(get_count(db, table_name))) ]) diff --git a/view/view_rows_import.anubis b/view/view_rows_import.anubis new file mode 100644 index 0000000..0793372 --- /dev/null +++ b/view/view_rows_import.anubis @@ -0,0 +1,214 @@ +/* + * Created by PyramIDE. + * User: フランスのトトロ aka (David RENÉ) + * Date: 06/05/2017 + * Time: 10:33 + * © Calexium + */ + +read locale/L3.anubis +read locale/L3LanguageInfo.anubis //date and time formating +read system/data_io.anubis +read tools/line_reader.anubis + +read calexium_lib/database/csv_tools.anubis +read calexium_lib/web/CXM_making_a_web_site.anubis +read calexium_lib/web/CXM_jquery.anubis +read calexium_lib/web/jQuery/CXM_jquery_button.anubis +read calexium_lib/web/widgets/css_helper.anubis +read calexium_lib/web/widgets/icons_set.anubis + +read hayamiki_lib/view/web_action.anubis + +read web_pages/desktop.anubis +read app/app_constants.anubis +read version.anubis + +public define List((List(CoreAttrs), WebArgValue, String)) + csv_delimiter_combo + ( + (String) -> String _T + ) + = + (List((List(CoreAttrs), WebArgValue, String)))[ + ([],wav(","), _T("COMA_SEP")), + ([],wav(";"), _T("SEMICOLON_SEP"))]. + + + +public define HTML_Partial_Content + import_zone + ( + String table_name, + (String) -> String _T + )= + html_wave_box(_T("CONTROL_PANEL"), + partial_content( + [ js(js_file("js/dropzone.js")), + js_inline(jquery_ready("Dropzone.options.myAwesomeDropzone = { + maxFiles: 1, + dictDefaultMessage : \""+_T("DROP_FILE_HERE")+"\", + dictFileTooBig : \""+_T("FILE_TOO_BIG")+"\", + }; + $('#my-awesome-dropzone').dropzone(); ")), + ], + + sequence([ + text([], _T("CSV_IMPORT_FOR")+" "+table_name), + form("my-awesome-dropzone",[class("dropzone")], hkc_import_file, + [("format","csv"), ("table_name",table_name)], + empty), + partial(jquery_button(jquery_img_button(icn16_cross, _T("CANCEL"), "", jQuery_actioner(same, jqlink(hkc_cancel)), left))), + partial(jquery_button(jquery_img_button(icn16_check, _T("IMPORT"), "", jQuery_actioner(same, jqlink(hkc_import_choose_col, [("format","csv"), ("table_name",table_name)])), left))) + ]) + ) + ) +. + +define List(HTML_Row(HTML_In_Form)) + source_lines + ( + List(String) cols, + List(HTML_Row(HTML_In_Form)) so_far, + Int index, + (String) -> String _T + )= + if cols is + { + [] then reverse(so_far), + [ h . t ] then + source_lines( t, [ row([cell([style("display:none")], literal("")), + cell([core_attrs([id("col_"+index), class("dnd-src")])], text(_T("COLUMN")+ " "+index+" ("+h+")"))]) . so_far], index+1, _T) + } + . + +public define HTML_In_Form + source_file_column_table + ( + Word8 separator, + String uploaded_file, + (String) -> String _T + )= + if file(uploaded_file, read) is + { + failure then + //logError(debug_log, "Can't open CSV file '" + f_name + "'"); text(_T("CANT_OPEN_FILE")), + println("Can't open CSV file '" + uploaded_file + "'"); + text(_T("CANT_OPEN_FILE")) + success(f) then + //logDebug(debug_log, "file opened, make_line_reader"); + //Parse the first line + if csv_read_line(make_data_io(f), separator, false, "", [], []) is + { + failure then + //logError(debug_log, "Can't parse 1st line of CSV file '" + f_name + "'"); + text(_T("PARSE_ERROR")), + success(col_data) then + //logDebug(debug_log, "read_line: read"); + + if col_data is (line, cols) then + sequence([ + hidden(html_Id("hk_source_col"), wan("hk_source_col"), wav(to_String(length(cols)))), + table([core_attrs([class("changelist small"), id("table-src")])], + header_row([ header_cell([style("display:none")], literal("")), + header_cell(literal("CSV_FILE_FIELDS"))]), + source_lines(cols, [], 0, _T) + ) + ]) + } + }. +. + +define List(HTML_Row(HTML_In_Form)) + target_lines + ( + List((String,String)) cols, + List(HTML_Row(HTML_In_Form)) so_far, + Int index, + (String) -> String _T + )= + //if tb is tool_box(lang, _, _T, _, db, user, _, _) then + if cols is + { + [] then reverse(so_far), + [ h . t ] then + since h is (hidden_name, text_field), + target_lines( t,[ row([ + cell([style("display:none")], empty), + cell([class("dnd-text ")], text(_T(text_field))), + cell([class("dnd-dst")], sequence([ + text(_T("IGNORED_COLUMN")), + hidden(html_Id("hk_"+hidden_name+"_input"), wan(""), wav(hidden_name)) + ])), + cell([class("icon ")],image([class("dnd-dst-btn")],"/icons/16x16/delete_cross.png","")) + ]) + . so_far], index+1, _T) + }. + +define HTML_In_Form + target_column_table + ( + List((String,String)) choices, + (String) -> String _T + )= + //since tb is tool_box(lang, _, _T, _, db, user, _, _), + + table([core_attrs([class("changelist small"), id("table-dst")])], + header_row([ header_cell([style("display:none")], empty), + header_cell(text(_T("TABLE_FIELDS"))), + header_cell(text(_T("IMPORTED_FIELDS"))), + header_cell(literal(" "))]), + target_lines(choices, [], 0, _T) + ) +. + +public define HTML_Partial_Content + import_choose_col + ( + String target_table_name, + List((String,String)) target_columns_choices, + String separator, + String uploaded_file, + WEB_Action_Name cancel_action, + (String) -> String _T, + )= + partial_content([css(css_file("css/cxm/cxm_dnd.css")), + js(js_file("js/cxm/cxm_dnd.js")), + js(js_file("js/data_import.js")) + ], + sequence([ + form("import_csv_form",[], hkc_import_execute, [("table_name", target_table_name), ("file",uploaded_file)], sequence([ + table([class("changelist small")], + header_row([ header_cell(literal(" ")), + header_cell(text(_T("INFORMATION_OF_SOURCE_FILE"))), + header_cell(literal(" ")), + ]), + [row([], [cell(image(icn16_mime_csv)), cell(text(_T("FILE_FORMAT"))), cell(text(_T("CSV_FILE")))]), + row([], [cell(image(icn16_mime_unknown)), cell(text(_T("SOURCE_FILE_TO_IMPORT"))), cell(text(uploaded_file))]), + row([], [cell(empty), cell(text(_T("DELIMITER"))), cell(selector_c([], no_label, html_Id("import_delimiter"), wan("csv_sep"), 1, csv_delimiter_combo(_T), init(separator)),)]), + row([], [cell(empty), cell(text(_T("FILE_ENCODING"))), cell(text("choose encoding"))]), + row([], [cell(empty), cell(text(_T("REMOVE_CSV_HEADER"))), cell(check_box([], no_label, html_Id(""), wan("hk_remove_csv_header"), wav("1"), true))]), +// row([], [cell(empty), cell(text(_T("OPT_OUT"))), cell(check_box([], no_label, html_Id(""), wan("import_opt_out"), wav("1"), false))])] //end of row + ]), + br, + div([id("dragLists")], + sequence([ + //SOURCE column + div([style("float: left; margin: 5px;")], div([class("dndContainer ")], + source_file_column_table(if explode(separator) is { [] then ';', [h . _] then h }, uploaded_file, _T))), + //TARGET column + div([style("float: left; margin: 5px;")], div([class("dndContainer ")], + target_column_table(target_columns_choices, _T))), + + div_empty([class("clear")]) + ])), + //partial(jquery_button(jquery_img_button(icn16_cross, _T("CANCEL"), "", jQuery_actioner(same, jqlink, "go_edit_mailing_list&list_id="+list_id.id), left))), + //partial(jquery_button(jquery_img_button(icn16_check, _T("IMPORT"), "", jQuery_actioner(same, jqform, "import_csv_form"), left))) + + ])), //end of sequence in form + // )]) + partial(jquery_button(jquery_img_button(icn16_cross, _T("CANCEL"), "", jQuery_actioner(same, jqlink(cancel_action)), left))), + partial(jquery_button(jquery_img_button(icn16_check, _T("IMPORT"), "", jQuery_actioner(same, jqform("import_csv_form")), left))) + ]) //end of sequence + ) +. diff --git a/view/view_table_renderer.anubis b/view/view_table_renderer.anubis index 1a8b42b..58f7144 100644 --- a/view/view_table_renderer.anubis +++ b/view/view_table_renderer.anubis @@ -178,7 +178,7 @@ define String autowidth:true, shrinkToFit:false, rowNum:30, - rowList:[5,10,30,50], + rowList:[5,10,20,30,50], cellEdit: false, cellsubmit: 'remote', cellurl: '?aws_controller=hk_c&aws_action=grid_action&table_name="+rows.table_name+"', @@ -592,8 +592,7 @@ public define HTML_Partial_Content jq_datetimepicker_init(lang) + get_editor_header(list_entries), //add editor header if need. Like init ck_editor sequence([ - cxm_form( table_name, lwa, - + cxm_form( table_name, lwa, [ one_line_fields([ submit(if create then hkc_save_row else hkc_update_row, failure, button_name, [("table_name", table_name)]), submit(if create then hkc_save_row_and_new else hkc_update_row_and_new, failure, button_name_and_new, [("table_name", table_name)]), @@ -624,8 +623,6 @@ public define HTML_Partial_Content ]) ] ), - - form("my-awesome-dropzone",[class("dropzone")], "upload_csv_file&step=2&format=csv&table_name="+table_name, [], literal("")), ] )). diff --git a/view/web_action.anubis b/view/web_action.anubis index 0fbd0f9..34111fb 100644 --- a/view/web_action.anubis +++ b/view/web_action.anubis @@ -9,7 +9,7 @@ read calexium_lib/web/CXM_making_a_web_site.anubis here are defined all action managed by Hayamiki Controller - + public define WEB_Action_Name hkc_edit_table = controller_action("hk_c", "edit_table"). public define WEB_Action_Name hkc_view_editor = controller_action("hk_c", "view_editor"). public define WEB_Action_Name hkc_grid_view = controller_action("hk_c", "grid_view"). @@ -23,3 +23,7 @@ public define WEB_Action_Name hkc_delete_row = controller_action("hk_c" public define WEB_Action_Name hkc_cancel = controller_action("hk_c", "cancel"). public define WEB_Action_Name hkc_ajax_edit_table = controller_action("hk_c", "ajax_edit_table"). public define WEB_Action_Name hkc_show_apps = controller_action("hk_c", "show_apps"). +public define WEB_Action_Name hkc_import_dropzone = controller_action("hk_c", "hkc_import_dz"). +public define WEB_Action_Name hkc_import_file = controller_action("hk_c", "hkc_import_file"). +public define WEB_Action_Name hkc_import_choose_col = controller_action("hk_c", "hkc_import_col"). +public define WEB_Action_Name hkc_import_execute = controller_action("hk_c", "hkc_import_exec"). -- libgit2 0.21.4