review-checklists/spreadsheet/Sheet1.vb

987 строки
47 KiB
VB.net

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