spreadsheet/Sheet1.vb (777 lines of code) (raw):

Option Explicit Option Base 0 Private Declare PtrSafe Function URLDownloadToFile Lib "urlmon" Alias "URLDownloadToFileA" (ByVal pCaller As Long, ByVal szURL As String, ByVal szFileName As String, ByVal dwReserved As Long, ByVal lpfnCB As Long) As Long 'Constants Const row1 = 10 Const technology_selection_row = 2 Const technology_selection_col = 7 Const language_selection_row = technology_selection_row Const language_selection_col = technology_selection_col + 1 Const checklist_name_row = 6 Const checklist_name_col = 2 Const checklist_state_row = checklist_name_row + 1 Const checklist_state_col = checklist_name_col Const checklist_timestamp_row = checklist_state_row + 1 Const checklist_timestamp_col = checklist_name_col + 1 Const line_break = vbCrLf Const id_column = 1 Const category_column = id_column + 1 Const subcategory_column = category_column + 1 Const waf_column = subcategory_column + 1 Const text_column = waf_column + 1 Const description_column = text_column + 1 Const severity_column = description_column + 1 Const status_column = severity_column + 1 Const comments_column = status_column + 1 Const link_column = comments_column + 1 Const training_column = link_column + 1 Const graph_column = training_column + 1 Const guid_column = graph_column + 1 Const sec_mod_column = guid_column + 1 Const cost_mod_column = sec_mod_column + 1 Const scale_mod_column = cost_mod_column + 1 Const simple_mod_column = scale_mod_column + 1 Const ha_mod_column = simple_mod_column + 1 Const num_columns = ha_mod_column Const values_sheet = "values" Const values_sev_column = 1 Const values_status_column = 2 Const values_status_description_column = 8 Const values_category_column = 3 Const values_technology_selection_column = 6 Const values_language_selection_column = 7 Const values_technology_prefix_column = 10 Const values_language_prefix_column = 11 Const values_waf_column = 12 Const row_limit = 2000 'Safety net Const user_info_guid_index = 1 Const user_info_comments_index = 2 Const user_info_status_index = 3 'Global variables Dim user_checks() As String 'To hold the user's comments/status Dim selected_language As String Dim selected_technology As String Dim import_graph_queries As Boolean 'Modifies text so that it can be stored in JSON format ' * Removes line breaks (chr10 and chr13) ' * Replaces doublequotes with single quotes ' * Eliminates blanks (replaces with &nbsp) Function correct_format_for_JSON(ByVal input_text As String) As String 'Remove breaks input_text = Replace(Replace(input_text, Chr(10), ""), Chr(13), "") 'Replace double quotes with single quotes input_text = Replace(input_text, Chr(34), Chr(39)) 'If it is a link, remove localization input_text = Replace(input_text, "en-us/", "") 'Escape control characters such as "?" 'input_text = escape_character(input_text, "?") input_text = escape_character(input_text, "\") input_text = escape_character(input_text, "\", False) 'Second escaping to turn '\' into '\\\\' 'Avoid empty fields, the Azure translate API doesnt work well with then If input_text = "" Then input_text = "&nbsp" correct_format_for_JSON = input_text End Function 'Escapes a character in a string, and gives out the resulting string Function escape_character(ByVal input_text As String, search_char As String, Optional unescape_first As Boolean = True) As String 'First we remove any possible escape signs for that character that might be already there If unescape_first Then input_text = Replace(input_text, "\" + search_char, search_char) End If 'Now we escape all occurrences of the character input_text = Replace(input_text, search_char, "\" + search_char) 'And return the output escape_character = input_text End Function 'Exports current checklist from excel to a json-formated text file Sub export_json() On Error GoTo errh ' Constants Const export_file_name = "checklist.json" ' Variables Dim row As Integer Dim json As String Dim check_id As String, check_category As String, check_subcategory As String, check_waf As String, check_text As String, check_description As String, check_severity As String, check_status As String, status_description As String, check_link As String, check_training As String, check_graph_query As String, check_guid As String Dim check_sec_mod, check_cost_mod, check_scale_mod, check_simple_mod, check_ha_mod Dim category_name As String, severity_name As String, status_name As String, waf_name As String Dim export_file_path As Variant Dim double_quote As String Dim item_count As Integer, category_count As Integer, status_count As Integer Dim cat_id As Integer, rel_subcat_id As Integer, rel_item_id As Integer ' Initialization double_quote = Chr(34) ' double quote as a variable row = row1 json = "{" + line_break json = json + " " + double_quote + "items" + double_quote + ": [" item_count = 0 category_count = 0 status_count = 0 cat_id = 0 rel_subcat_id = 0 rel_item_id = 0 check_category = "" check_subcategory = "" ' Loop through all rows as long as there is content Do While row < row_limit And Len(Cells(row, category_column)) > 0 If row > row1 Then json = json + "," json = json + line_break 'Update ID counters If Cells(row, category_column) <> check_category Then 'New category cat_id = cat_id + 1 rel_subcat_id = 1 rel_item_id = 1 Else 'New subcategory If Cells(row, subcategory_column) <> check_subcategory Then rel_subcat_id = rel_subcat_id + 1 rel_item_id = 1 Else 'Same category and subcategory rel_item_id = rel_item_id + 1 End If End If 'Get values check_id = Cells(row, id_column) check_category = Cells(row, category_column) check_subcategory = Cells(row, subcategory_column) check_waf = Cells(row, waf_column) check_text = Cells(row, text_column) check_description = Cells(row, description_column) check_severity = Cells(row, severity_column) check_graph_query = Cells(row, graph_column) check_guid = LCase(Cells(row, guid_column)) check_sec_mod = Cells(row, sec_mod_column) check_cost_mod = Cells(row, cost_mod_column) check_scale_mod = Cells(row, scale_mod_column) check_simple_mod = Cells(row, simple_mod_column) check_ha_mod = Cells(row, ha_mod_column) 'If there was no GUID, generate one If Len(check_guid) = 0 Then check_guid = generate_guid() 'If there was no ID, generate one with the cat/subcat/item IDs If Len(check_id) = 0 Then check_id = Trim(Str(cat_id)) & "." & Trim(Str(rel_subcat_id)) & "." & Trim(Format(rel_item_id, "00")) 'Only read More Info hyperlink if the cell contains one If Cells(row, link_column).Hyperlinks.Count > 0 Then 'Anchors are not contained by the Address property, we need to concat the Subaddress too If Len(Cells(row, link_column).Hyperlinks(1).SubAddress) > 0 Then check_link = Cells(row, link_column).Hyperlinks(1).Address + "#" + Cells(row, link_column).Hyperlinks(1).SubAddress Else check_link = Cells(row, link_column).Hyperlinks(1).Address End If Else check_link = Cells(row, link_column) End If 'Only read Training hyperlink if the cell contains one If Cells(row, training_column).Hyperlinks.Count > 0 Then check_training = Cells(row, training_column).Hyperlinks(1).Address Else check_training = Cells(row, training_column) End If row = row + 1 json = json + " {" + line_break json = json + " " + double_quote + "category" + double_quote + ": " + double_quote + correct_format_for_JSON(check_category) + double_quote + "," json = json + line_break + " " + double_quote + "subcategory" + double_quote + ": " + double_quote + correct_format_for_JSON(check_subcategory) + double_quote json = json + "," + line_break + " " + double_quote + "text" + double_quote + ": " + double_quote + correct_format_for_JSON(check_text) + double_quote If Len(check_description) > 0 Then json = json + "," + line_break + " " + double_quote + "description" + double_quote + ": " + double_quote + correct_format_for_JSON(check_description) + double_quote If Len(check_waf) > 0 Then json = json + "," + line_break + " " + double_quote + "waf" + double_quote + ": " + double_quote + correct_format_for_JSON(check_waf) + double_quote If Len(check_guid) > 0 Then json = json + "," + line_break + " " + double_quote + "guid" + double_quote + ": " + double_quote + check_guid + double_quote If Len(check_id) > 0 Then json = json + "," + line_break + " " + double_quote + "id" + double_quote + ": " + double_quote + check_id + double_quote If Len(check_sec_mod) > 0 And IsNumeric(check_sec_mod) Then json = json + "," + line_break + " " + double_quote + "security" + double_quote + ": " + CStr(check_sec_mod) If Len(check_cost_mod) > 0 And IsNumeric(check_cost_mod) Then json = json + "," + line_break + " " + double_quote + "cost" + double_quote + ": " + CStr(check_cost_mod) If Len(check_scale_mod) > 0 And IsNumeric(check_scale_mod) Then json = json + "," + line_break + " " + double_quote + "scale" + double_quote + ": " + CStr(check_scale_mod) If Len(check_simple_mod) > 0 And IsNumeric(check_simple_mod) Then json = json + "," + line_break + " " + double_quote + "simple" + double_quote + ": " + CStr(check_simple_mod) If Len(check_ha_mod) > 0 And IsNumeric(check_ha_mod) Then json = json + "," + line_break + " " + double_quote + "ha" + double_quote + ": " + CStr(check_ha_mod) If Len(check_severity) > 0 Then json = json + "," + line_break + " " + double_quote + "severity" + double_quote + ": " + double_quote + check_severity + double_quote If Len(check_graph_query) > 0 Then json = json + "," + line_break + " " + double_quote + "graph" + double_quote + ": " + double_quote + correct_format_for_JSON(check_graph_query) + double_quote If Len(check_training) > 0 Then json = json + "," + line_break + " " + double_quote + "training" + double_quote + ": " + double_quote + correct_format_for_JSON(check_training) + double_quote If Len(check_link) > 0 Then json = json + "," + line_break + " " + double_quote + "link" + double_quote + ": " + double_quote + correct_format_for_JSON(check_link) + double_quote json = json + line_break + " }" item_count = item_count + 1 Loop ' Finish items section json = json + line_break + " ]," + line_break ' Create categories section json = json + " " + double_quote + "categories" + double_quote + ": [" row = 2 Do While row < row_limit And Len(Sheets(values_sheet).Cells(row, values_category_column)) > 0 If row > 2 Then json = json + "," json = json + line_break category_name = Sheets(values_sheet).Cells(row, values_category_column) json = json + " {" + line_break json = json + " " + double_quote + "name" + double_quote + ": " + double_quote + correct_format_for_JSON(category_name) + double_quote + line_break json = json + " }" row = row + 1 category_count = category_count + 1 Loop ' Finish category section json = json + line_break + " ]," + line_break ' Create WAF section json = json + " " + double_quote + "waf" + double_quote + ": [" row = 2 Do While row < row_limit And Len(Sheets(values_sheet).Cells(row, values_waf_column)) > 0 If row > 2 Then json = json + "," json = json + line_break waf_name = Sheets(values_sheet).Cells(row, values_waf_column) json = json + " {" + line_break json = json + " " + double_quote + "name" + double_quote + ": " + double_quote + correct_format_for_JSON(waf_name) + double_quote + line_break json = json + " }" row = row + 1 category_count = category_count + 1 Loop ' Finish WAF section json = json + line_break + " ]," + line_break ' Create status section json = json + " " + double_quote + "status" + double_quote + ": [" row = 2 Do While row < row_limit And Len(Sheets(values_sheet).Cells(row, values_status_column)) > 0 If row > 2 Then json = json + "," json = json + line_break status_name = Sheets(values_sheet).Cells(row, values_status_column) status_description = Sheets(values_sheet).Cells(row, values_status_description_column) json = json + " {" + line_break json = json + " " + double_quote + "name" + double_quote + ": " + double_quote + correct_format_for_JSON(status_name) + double_quote + "," + line_break json = json + " " + double_quote + "description" + double_quote + ": " + double_quote + correct_format_for_JSON(status_description) + double_quote + line_break json = json + " }" row = row + 1 status_count = status_count + 1 Loop ' Finish status section json = json + line_break + " ]," + line_break ' Create severities section json = json + " " + double_quote + "severities" + double_quote + ": [" row = 2 Do While row < row_limit And Len(Sheets(values_sheet).Cells(row, values_sev_column)) > 0 If row > 2 Then json = json + "," json = json + line_break severity_name = Sheets(values_sheet).Cells(row, values_sev_column) json = json + " {" + line_break json = json + " " + double_quote + "name" + double_quote + ": " + double_quote + correct_format_for_JSON(severity_name) + double_quote + line_break json = json + " }" row = row + 1 Loop ' Finish severities section json = json + line_break + " ]," + line_break ' Create metadata section json = json + " " + double_quote + "metadata" + double_quote + ": {" + line_break json = json + " " + double_quote + "name" + double_quote + ": " + double_quote + correct_format_for_JSON(Cells(checklist_name_row, checklist_name_col)) + double_quote + "," + line_break json = json + " " + double_quote + "state" + double_quote + ": " + double_quote + correct_format_for_JSON(Cells(checklist_state_row, checklist_state_col)) + double_quote + "," + line_break json = json + " " + double_quote + "timestamp" + double_quote + ": " + double_quote + correct_format_for_JSON(Format(Now, "mm/dd/yyyy HH:mm:ss")) + double_quote + line_break ' Finish metadata section json = json + " }" + line_break ' Finish JSON json = json + "}" + line_break ' Write JSON to file ' MsgBox json 'export_file_path = ActiveWorkbook.Path + "\" + export_file_name export_file_path = "" export_file_path = Application.GetSaveAsFilename(FileFilter:="JSON File (*.json), *.json", Title:="Exporting JSON checklist", InitialFileName:=ActiveWorkbook.Path + "\" + export_file_name) 'checks to make sure the user hasn't canceled the dialog If export_file_path <> False Then 'MsgBox "Exporting to " + export_file_path Open export_file_path For Output As #1 Print #1, json Close #1 End If MsgBox CStr(item_count) + " checklist items and " + CStr(category_count) + " categories exported to JSON file " + export_file_path, vbInformation Exit Sub errh: If Err.Number <> 0 Then MsgBox "Error while exporting checklist to JSON " & Err.Description, vbCritical End If End Sub 'Import JSON from URL (first download to a local file) Sub import_checklist_fromurl() Const values_sheet = "values" Const values_url_column = 4 Dim checklist_url, checklist_base_url As String Dim json_file As Variant Dim buf, ret As Long Dim url_split() As String Dim filename As String Dim objXmlHttpReq As Object Dim objStream As Object Dim msg As String 'Get URL stored in the Values sheet checklist_base_url = Sheets(values_sheet).Cells(2, values_url_column) If Len(checklist_base_url) > 0 Then 'Look at the option buttons to set global variables that control technology (LZ, AKS, AVD, AVS, Security) and language set_checklist_variables 'Append the technology and options variables (default to English and LZ) If Len(selected_technology) = 0 Then selected_technology = "alz" If Len(selected_language) = 0 Then selected_language = "en" checklist_url = checklist_base_url + selected_technology + "_checklist." + selected_language + ".json" 'Get the filename of the URL url_split = Split(checklist_url, "/") filename = url_split(UBound(url_split)) ' Instead of letting the user specify a download directory, picking up Downloads per default 'ChDir ActiveWorkbook.Path 'json_file = Application.GetOpenFilename(Title:="Please choose a file to open", FileFilter:="JSON Files *.json* (*.json),") json_file = Environ("USERPROFILE") + "\Downloads\checklist.json" msg = "Reference checklist will be downloaded from '" + checklist_url + "' to '" + CStr(json_file) + "'" If MsgBox(msg, vbOKCancel + vbQuestion, "Downloading reference checklist") = vbOK Then ret = URLDownloadToFile(0, checklist_url, json_file, 0, 0) If ret = 0 Then 'Call the sub for actually doing the import import_checklist_fromfile json_file Else MsgBox "Checklist could not be downloaded from '" + checklist_url + "'", vbCritical End If End If Else MsgBox "Sorry, I could not found out the reference URL in sheet '" + values_sheet + "' at cell location 2," + CStr(values_url_column), vbCritical End If End Sub 'Import from custom JSON file Sub import_checklist() On Error GoTo import_checklist_err Dim json_file As Variant ChDir ActiveWorkbook.Path json_file = Application.GetOpenFilename(Title:="Please choose a file to open", FileFilter:="JSON Files *.json* (*.json),") If json_file = False Then MsgBox "No file selected.", vbExclamation, "Action canceled" Exit Sub Else import_graph_queries = True 'Always import graph queries in advanced mode. import_checklist_fromfile json_file End If import_checklist_err: If Err.Number = 76 Then 'path not found, this happens usually if the spreadsheet is in onedrive Resume Next ElseIf Err.Number <> 0 Then MsgBox "Error while importing checklist from custom file: " & CStr(Err.Number) & ": " & Err.Description, vbCritical End If Exit Sub End Sub 'Import from a local JSON file 'Parse JSON code using the JsonConverter module Sub import_checklist_fromfile(json_file As Variant) On Error GoTo import_checklist_fromfile_err ' Constants Const row_limit = 2000 'Safety net 'Variables Dim json_ts As TextStream Dim FSO As New FileSystemObject Dim textline As String Dim json As String Dim json_object, json_item As Object Dim double_quote As String Dim line_elements() As String Dim row, item_count As Integer, category_count As Integer, waf_count As Integer, status_count, i As Integer Dim notverified As String Dim get_user_info_successful As Boolean Dim start_time As Double 'Remember time when macro starts start_time = Timer 'Disable some stuff to speed up Call DisableStuff 'Variable value initialization row = row1 double_quote = Chr(34) ' double quote as a variable json = "" item_count = 0 category_count = 0 waf_count = 0 status_count = 0 'First of all, get the info entered by the user in a global variable get_user_info_successful = get_user_input() 'Go through the file line by line Dim objStream Set objStream = CreateObject("ADODB.Stream") objStream.Charset = "utf-8" objStream.Open objStream.LoadFromFile (json_file) json = objStream.ReadText() objStream.Close Set objStream = Nothing json = Replace(json, vbCrLf, "") Set json_object = JsonConverter.ParseJson(json) 'This line might give Run-time error 10001 if there are JSON syntactic errors 'Update checklist title If json_object.Exists("metadata") Then If json_object("metadata").Exists("name") Then Cells(checklist_name_row, checklist_name_col) = json_object("metadata")("name") End If If json_object("metadata").Exists("state") Then Cells(checklist_state_row, checklist_state_col) = json_object("metadata")("state") End If If json_object("metadata").Exists("timestamp") Then Cells(checklist_timestamp_row, checklist_timestamp_col) = json_object("metadata")("timestamp") End If End If 'Import status If json_object.Exists("status") Then row = 2 If TypeName(json_object("status")) = "Dictionary" Then notverified = json_object("status")("0")("name") ' The "Not Verified" status is the first one i = 0 Do Sheets(values_sheet).Cells(row, values_status_column) = json_object("status")(CStr(i))("name") If json_object("status")(CStr(i)).Exists("description") Then Sheets(values_sheet).Cells(row, values_status_description_column) = json_object("status")(CStr(i))("description") End If row = row + 1 status_count = status_count + 1 i = i + 1 Loop While json_object("status").Exists(CStr(i)) Else notverified = json_object("status")(1)("name") ' The "Not Verified" status is the first one For Each json_item In json_object("status") Sheets(values_sheet).Cells(row, values_status_column) = json_item("name") If json_item.Exists("description") Then Sheets(values_sheet).Cells(row, values_status_description_column) = json_item("description") End If row = row + 1 status_count = status_count + 1 Next json_item End If 'Blank the rest of the status rows, although for status this shouldnt be required Do While row < row_limit And Len(Cells(row, values_status_column)) > 0 Sheets(values_sheet).Cells(row, values_status_column) = "" row = row + 1 Loop End If 'Import checklist items row = row1 'If JSON is a translated file, it is a nested dictionary with keys "0", "1", etc If TypeName(json_object("items")) = "Dictionary" Then i = 0 Do update_row json_object("items")(CStr(i)), row, notverified, get_user_info_successful row = row + 1 item_count = item_count + 1 i = i + 1 Loop While json_object("items").Exists(CStr(i)) 'Otherwise, it is just an array Else For Each json_item In json_object("items") update_row json_item, row, notverified, get_user_info_successful row = row + 1 item_count = item_count + 1 Next json_item End If 'Blank the rest of the item rows Do While row < row_limit And (Len(Cells(row, category_column)) + Len(Cells(row, subcategory_column)) + Len(Cells(row, text_column)) + Len(Cells(row, status_column)) + Len(Cells(row, comments_column))) > 0 Cells(row, id_column) = "" Cells(row, category_column) = "" Cells(row, subcategory_column) = "" Cells(row, waf_column) = "" Cells(row, text_column) = "" Cells(row, description_column) = "" Cells(row, severity_column) = "" Cells(row, link_column) = "" Cells(row, status_column) = "" Cells(row, comments_column) = "" Cells(row, training_column) = "" Cells(row, graph_column) = "" Cells(row, guid_column) = "" row = row + 1 Loop 'Import categories If json_object.Exists("categories") Then row = 2 If TypeName(json_object("categories")) = "Dictionary" Then i = 0 Do Sheets(values_sheet).Cells(row, values_category_column) = json_object("categories")(CStr(i))("name") row = row + 1 category_count = category_count + 1 i = i + 1 Loop While json_object("categories").Exists(CStr(i)) Else For Each json_item In json_object("categories") Sheets(values_sheet).Cells(row, values_category_column) = json_item("name") row = row + 1 category_count = category_count + 1 Next json_item End If 'Blank the rest of the category rows Do While row < row_limit And Len(Cells(row, values_category_column)) > 0 Sheets(values_sheet).Cells(row, values_category_column) = "" row = row + 1 Loop End If 'Import WAF pillars If json_object.Exists("waf") Then row = 2 If TypeName(json_object("waf")) = "Dictionary" Then i = 0 Do Sheets(values_sheet).Cells(row, values_waf_column) = json_object("waf")(CStr(i))("name") row = row + 1 waf_count = waf_count + 1 i = i + 1 Loop While json_object("waf").Exists(CStr(i)) Else For Each json_item In json_object("waf") Sheets(values_sheet).Cells(row, values_waf_column) = json_item("name") row = row + 1 waf_count = waf_count + 1 Next json_item End If 'Blank the rest of the category rows Do While row < row_limit And Len(Cells(row, values_waf_column)) > 0 Sheets(values_sheet).Cells(row, values_waf_column) = "" row = row + 1 Loop End If 'Import severities If json_object.Exists("severities") Then row = 2 If TypeName(json_object("severities")) = "Dictionary" Then i = 0 Do Sheets(values_sheet).Cells(row, values_sev_column) = json_object("severities")(CStr(i))("name") row = row + 1 i = i + 1 Loop While json_object("severities").Exists(CStr(i)) Else For Each json_item In json_object("severities") Sheets(values_sheet).Cells(row, values_sev_column) = json_item("name") row = row + 1 Next json_item End If 'Blank the rest of the severity rows Do While row < row_limit And Len(Cells(row, values_sev_column)) > 0 Sheets(values_sheet).Cells(row, values_sev_column) = "" row = row + 1 Loop End If MsgBox CStr(item_count) + " check items and " + CStr(category_count) + " categories imported from JSON file " + json_file + " in " + CStr(Round(Timer - start_time, 2)) + " seconds", vbInformation Call EnableStuff Exit Sub import_checklist_fromfile_err: If Err.Number <> 0 Then MsgBox "Error while importing checklist from JSON file " & json_file & ": " & Err.Description, vbCritical Call EnableStuff End If End Sub 'Function with error control in case a property does not exist for an object Function get_object_property(ByVal object_item As Object, property_name As String) As String On Error GoTo get_object_property_err Dim aux As String aux = object_item(property_name) 'If it is "&nbsp" or something similar (some translation engines introduce blanks between "&" and "nbsp"), set to null If Right(aux, 4) = "nbsp" Then aux = "" get_object_property = aux Exit Function get_object_property_err: get_object_property = "" Exit Function End Function ' Updates a checklist row with the information in the object Sub update_row(ByVal json_item As Object, ByVal row As Integer, notverified As String, user_input_saved As Boolean) On Error GoTo update_row_err ' Variables and constants Dim check_id As String, check_category As String, check_subcategory As String, check_waf As String, check_text As String, check_description As String, check_severity As String, check_status As String, check_link As String, check_training As String, check_graph_query As String, check_guid As String Dim user_input_comments As String, user_input_status As String Dim check_sec_mod, check_cost_mod, check_scale_mod, check_simple_mod, check_ha_mod Dim get_guid_user_input_successful As Boolean Dim newrow(1 To num_columns) As Variant ' Defaults to English "Not Verified" If Len(notverified) = 0 Then notverified = "Not verified" ' Code check_id = get_object_property(json_item, "id") check_category = get_object_property(json_item, "category") check_subcategory = get_object_property(json_item, "subcategory") check_waf = get_object_property(json_item, "waf") check_text = get_object_property(json_item, "text") check_description = get_object_property(json_item, "description") check_severity = get_object_property(json_item, "severity") check_link = get_object_property(json_item, "link") check_training = get_object_property(json_item, "training") check_graph_query = get_object_property(json_item, "graph") If Len(check_graph_query) = 0 Then check_graph_query = get_object_property(json_item, "graph_success") 'Backwards compatibility check_guid = get_object_property(json_item, "guid") check_sec_mod = get_object_property(json_item, "security") check_cost_mod = get_object_property(json_item, "cost") check_scale_mod = get_object_property(json_item, "scale") check_simple_mod = get_object_property(json_item, "simple") check_ha_mod = get_object_property(json_item, "ha") 'Create array variable with the information of the provided JSON object newrow(id_column) = check_id newrow(category_column) = check_category newrow(subcategory_column) = check_subcategory newrow(waf_column) = check_waf newrow(text_column) = check_text newrow(description_column) = check_description newrow(severity_column) = check_severity newrow(guid_column) = check_guid 'If the previous user input was saved, and we can successfully retrieve an entry for this guid, put it in the comments/status fields If user_input_saved Then If get_user_input_from_guid(check_guid, user_input_comments, user_input_status) Then newrow(status_column) = CStr(user_input_status) 'Some times around this line there is a dreadful 400 error :( newrow(comments_column) = CStr(user_input_comments) Else newrow(status_column) = notverified '"notverified" is a variable containing the translation of "not verified" newrow(comments_column) = "" End If 'Otherwise, blank them Else newrow(status_column) = notverified '"notverified" is a variable containing the translation of "not verified" newrow(comments_column) = "" End If 'Graph queries can optionally be imported or not, since it impacts readability 'Per default, they are only imported when using the English language If import_graph_queries Then newrow(graph_column) = check_graph_query Else newrow(graph_column) = "" End If 'WAF Pillar modifiers are optional too If IsNumeric(check_sec_mod) Then newrow(sec_mod_column) = check_sec_mod End If If IsNumeric(check_cost_mod) Then newrow(cost_mod_column) = check_cost_mod End If If IsNumeric(check_scale_mod) Then newrow(scale_mod_column) = check_scale_mod End If If IsNumeric(check_simple_mod) Then 'Cells(row, simple_mod_column) = check_simple_mod newrow(simple_mod_column) = check_simple_mod End If If IsNumeric(check_ha_mod) Then newrow(ha_mod_column) = check_ha_mod End If 'Update Excel full row with the array in one go Range(Cells(row, 1), Cells(row, num_columns)).Value = newrow 'Training and MoreInfo link optional If Len(check_link) > 0 Then Cells(row, link_column).Hyperlinks.Add Address:=check_link, TextToDisplay:="More info", Anchor:=Cells(row, link_column), ScreenTip:=check_link Else Cells(row, link_column) = "" End If If Len(check_training) > 0 Then Cells(row, training_column).Hyperlinks.Add Address:=check_training, TextToDisplay:="Training", Anchor:=Cells(row, training_column), ScreenTip:=check_training Else Cells(row, training_column) = "" End If Exit Sub update_row_err: If Err.Number <> 0 Then MsgBox "Error while updating row: " & Err.Description, vbCritical End If End Sub 'Generates a random GUID 'https://danwagner.co/how-to-generate-a-guid-in-excel-with-vba/ Function generate_guid() As String On Error GoTo genguiderr Dim k, h As Variant generate_guid = Space(36) For k = 1 To Len(generate_guid) Randomize Select Case k Case 9, 14, 19, 24: h = "-" Case 15: h = "4" Case 20: h = Hex(Rnd * 3 + 8) Case Else: h = Hex(Rnd * 15) End Select Mid$(generate_guid, k, 1) = h Next generate_guid = LCase$(generate_guid) genguiderr: If Err.Number <> 0 Then MsgBox "Error while generating GUID: " & Err.Description, vbCritical End If End Function 'Import Azure Resource Graph query results from a JSON file generated with checklist_graph.sh Sub import_graph_results() On Error GoTo import_graph_results_err Dim json_file As Variant Dim objStream Dim json As String Dim json_object, json_item As Object Dim check_guid As String, check_success_result As String, check_failure_result As String, check_result As String, check_arm_id As String Dim row As Integer Dim query_result_format As String, query_result_date As String 'File pick dialog ChDir ActiveWorkbook.Path json_file = Application.GetOpenFilename(Title:="Please choose a file to open", FileFilter:="JSON Files *.json* (*.json),") If json_file = False Then MsgBox "No file selected.", vbExclamation, "Action canceled" Exit Sub Else If MsgBox("This action will potentially overwrite existing contents in the Comments column. Do you want to continue?", vbQuestion + vbYesNo) = vbYes Then 'Open and read text file Set objStream = CreateObject("ADODB.Stream") objStream.Charset = "utf-8" objStream.Open objStream.LoadFromFile (json_file) json = objStream.ReadText() objStream.Close Set objStream = Nothing 'Remove line breaks and parse json = Replace(json, vbCrLf, "") Set json_object = JsonConverter.ParseJson(json) 'This line might give Run-time error 10001 if there are JSON syntax errors 'Browse the checks one per one If json_object.Exists("checks") Then For Each json_item In json_object("checks") 'Get the check info check_guid = get_object_property(json_item, "guid") check_result = get_object_property(json_item, "compliant") check_arm_id = get_object_property(json_item, "id") 'Now find the row with a matching GUID, and populate the comments field row = row1 Do Until Cells(row, guid_column) = check_guid Or Cells(row, text_column) = "" row = row + 1 Loop 'And update the comments column if it was found If Cells(row, text_column) = "" Then MsgBox "GUID " & check_guid & " not found in this checklist.", vbExclamation Else 'Enter a line break if the cell wasnt empty If Len(Cells(row, comments_column)) > 0 Then Cells(row, comments_column) = Cells(row, comments_column) & Chr(10) 'Depending on the test result, add 'compliant' or 'non-compliant' If check_result = "true" Then Cells(row, comments_column) = Cells(row, comments_column) & "Compliant: " + check_arm_id ElseIf check_result = "false" Then Cells(row, comments_column) = Cells(row, comments_column) & "Non-compliant: " + check_arm_id Else MsgBox "Check result " & check_result & " not understood", vbExclamation End If End If Next json_item Else MsgBox "It looks like the JSON file " & json_file & " does not contain a 'checks' object?", vbCritical End If End If End If Exit Sub import_graph_results_err: If Err.Number = 10001 Then MsgBox "Error while importing graph results: please verify that the file " & json_file & " has a valid JSON format", vbCritical ElseIf Err.Number = 76 Then 'path not found, this happens usually if the spreadsheet is in onedrive Resume Next ElseIf Err.Number <> 0 Then MsgBox "Error while importing graph results: " & CStr(Err.Number) & ": " & Err.Description, vbCritical End If Exit Sub End Sub 'Clear all rows Sub clear_rows() Dim row As Integer Dim emptyrow() As Variant, i As Integer ReDim Preserve emptyrow(1 To num_columns) row = row1 For i = 1 To num_columns emptyrow(i) = "" Next i Do While row < row_limit And (Len(Cells(row, category_column)) + Len(Cells(row, subcategory_column)) + Len(Cells(row, text_column)) + Len(Cells(row, status_column)) + Len(Cells(row, comments_column))) > 0 Range(Cells(row, 1), Cells(row, num_columns)).Value = emptyrow row = row + 1 Loop 'Delete categories row = 2 Do While row < row_limit And Len(Sheets(values_sheet).Cells(row, values_category_column)) > 0 Sheets(values_sheet).Cells(row, values_category_column) = "" row = row + 1 Loop 'Reset checklist title Cells(checklist_name_row, checklist_name_col) = Sheets(values_sheet).Cells(2, 5) 'Delete status and timestamp Cells(checklist_state_row, checklist_state_col) = "" Cells(checklist_timestamp_row, checklist_timestamp_col) = "" 'Defaults to LZ and English Cells(technology_selection_row, technology_selection_col) = Sheets(values_sheet).Cells(2, values_technology_selection_column) Cells(language_selection_row, language_selection_col) = Sheets(values_sheet).Cells(2, values_language_selection_column) End Sub 'Delete all from controls, so that the spreadsheet can be saved as macro-free Sub delete_controls() Dim item As Object 'Browse all shapes For Each item In ActiveSheet.Shapes 'Only Form Controls If item.Type = msoFormControl Then item.Delete End If Next item End Sub 'Inspect option buttons and set global variables accordingly Sub set_checklist_variables() On Error GoTo set_checklist_variables_err Dim tech_found As Boolean, lang_found As Boolean Dim row As Integer 'Initialization tech_found = False lang_found = False 'Find the prefix for the given technology row = 2 Do While row < row_limit And Len(Sheets(values_sheet).Cells(row, values_technology_selection_column)) > 0 If Sheets(values_sheet).Cells(row, values_technology_selection_column) = Cells(technology_selection_row, technology_selection_col) Then tech_found = True selected_technology = Sheets(values_sheet).Cells(row, values_technology_prefix_column) Exit Do End If row = row + 1 Loop 'Find the prefix for the given language row = 2 Do While row < row_limit And Len(Sheets(values_sheet).Cells(row, values_language_selection_column)) > 0 If Sheets(values_sheet).Cells(row, values_language_selection_column) = Cells(language_selection_row, language_selection_col) Then lang_found = True selected_language = Sheets(values_sheet).Cells(row, values_language_prefix_column) Exit Do End If row = row + 1 Loop ' 'Look at the technology cell ' Select Case Cells(technology_selection_row, technology_selection_col) ' Case Sheets(values_sheet).Cells(2, values_technology_selection_column) ' selected_technology = "alz" ' Case Sheets(values_sheet).Cells(3, values_technology_selection_column) ' selected_technology = "aks" ' Case Sheets(values_sheet).Cells(4, values_technology_selection_column) ' selected_technology = "avd" ' Case Sheets(values_sheet).Cells(5, values_technology_selection_column) ' selected_technology = "avs" ' Case Sheets(values_sheet).Cells(6, values_technology_selection_column) ' selected_technology = "security" ' Case Else ' MsgBox "Technology option " & Cells(technology_selection_row, technology_selection_col) & " unknown, defaulting to Landing Zone review", vbCritical ' selected_technology = "alz" ' End Select ' 'Look at the language cell ' Select Case Cells(language_selection_row, language_selection_col) ' Case Sheets(values_sheet).Cells(2, values_language_selection_column) ' selected_language = "en" ' import_graph_queries = True ' Case Sheets(values_sheet).Cells(3, values_language_selection_column) ' selected_language = "ja" ' import_graph_queries = False ' Case Sheets(values_sheet).Cells(4, values_language_selection_column) ' selected_language = "ko" ' import_graph_queries = False ' Case Sheets(values_sheet).Cells(5, values_language_selection_column) ' selected_language = "pt" ' import_graph_queries = False ' Case Sheets(values_sheet).Cells(6, values_language_selection_column) ' selected_language = "es" ' import_graph_queries = False ' Case Else ' MsgBox "Language option " & Cells(language_selection_row, language_selection_col) & " unknown, defaulting to English", vbCritical ' selected_language = "en" ' import_graph_queries = True ' End Select 'Import Azure Resource Graph queries only if the language is English (otherwise the translated queries are imported) 'If Cells(language_selection_row, language_selection_col) = "English" Then ' import_graph_queries = True 'Else ' MsgBox "Azure Resource Graph queries will not be imported, since the selected language is not English", vbInformation + vbOKOnly 'End If 'After translation has been fixed, we can now import the Graph queries safely :) import_graph_queries = True 'Error message if tech or lang could not be found If Not tech_found Then MsgBox "Not able to resolve technology '" + Cells(technology_selection_row, technology_selection_col) + "' to a file prefix", vbCritical End If If Not lang_found Then MsgBox "Not able to resolve language '" + Cells(language_selection_row, language_selection_col) + "' to a file prefix", vbCritical End If 'Debug.Print "Selected technology " + selected_technology + ", selected language " + selected_language Exit Sub set_checklist_variables_err: MsgBox "Error while reading checklist selection options, defaulting to Landing Zone and English: " & CStr(Err.Number) & ": " & Err.Description, vbCritical selected_technology = "alz" selected_language = "en" import_graph_queries = True End Sub 'Load in an object variable the existing values Function get_user_input() As Boolean On Error GoTo get_user_input_err Dim row, checks_counter As Integer row = row1 ReDim user_checks(3, 1) As String Erase user_checks checks_counter = 0 'Browse through all rows Do While row < row_limit And Len(Cells(row, category_column)) > 0 'Only store a check if there is a non-blank comment or status is different than "Not Verified" If Len(Cells(row, comments_column)) > 0 Or Cells(row, status_column) <> Sheets(values_sheet).Cells(2, values_status_column) Then checks_counter = checks_counter + 1 ReDim Preserve user_checks(3, checks_counter) As String user_checks(user_info_guid_index, checks_counter) = Cells(row, guid_column) user_checks(user_info_comments_index, checks_counter) = Cells(row, comments_column) user_checks(user_info_status_index, checks_counter) = Cells(row, status_column) End If row = row + 1 Loop get_user_input = True Exit Function get_user_input_err: If Err.Number <> 0 Then MsgBox "Error while reading existing comments: " & Err.Description, vbCritical End If get_user_input = False Exit Function End Function 'Return the comments a status for a certain guid in an bidimensional array generated by get_user_input Function get_user_input_from_guid(guid As String, ByRef comments As String, ByRef status As String) As Boolean On Error GoTo get_user_input_from_guid_err Dim i As Integer comments = "" status = "" For i = 1 To UBound(user_checks(), 2) If LCase(user_checks(user_info_guid_index, i)) = LCase(guid) Then comments = user_checks(user_info_comments_index, i) status = user_checks(user_info_status_index, i) get_user_input_from_guid = True Exit Function End If Next i get_user_input_from_guid = False Exit Function get_user_input_from_guid_err: If Err.Number = 9 Then 'Subscript out of range: silently exit get_user_input_from_guid = False Exit Function ElseIf Err.Number <> 0 Then Debug.Print "Error while retrieving comments/status for GUID " + guid + " - " & CStr(Err.Number) & ": " & Err.Description, vbCritical End If get_user_input_from_guid = False Exit Function End Function 'Disable some settings to speed up code Sub DisableStuff() Application.Calculation = xlCalculationManual Application.ScreenUpdating = False End Sub 'Enable settings back to normal Sub EnableStuff() Application.Calculation = xlCalculationAutomatic Application.ScreenUpdating = True End Sub 'Test sub Sub test() Dim comments As String, status As String If get_user_input() Then 'Debug.Print CStr(UBound(my_checks, 2) - LBound(my_checks, 2)) If get_user_input_from_guid("a0f61565-9de5-458f-a372-49c831112dbd", comments, status) Then Debug.Print "Comments: " + comments + ". Status: " + status End If End If End Sub