-- The functions and variables in this file are only for use in the mod itself. -- Those that do real work should be local and wrapped in public functions local function log(text) local logmessage = yl_survey.t("log_prefix", yl_survey.modname, text) if yl_survey.settings.debug then minetest.log("action", logmessage) end return logmessage end function yl_survey.log(text) return log(text) end -- Helpers local function filename2uuid(filename) return filename:sub(1, -6) end local function is_visible(filename) return (string.sub(filename, 1, 1) ~= ".") end local function is_json(filename) return (filename:match("%.json$")) end local function ends_with(str, suffix) return str:sub(-suffix:len()) == suffix end local function split(str) local parts = {} for part in str:gmatch("[^,%s]+") do table.insert(parts, part) end return parts end local function priv_exists(priv) return (minetest.registered_privileges[priv] ~= nil) or false end function yl_survey.priv_exists(priv) return priv_exists(priv) end -- table.contains local function table_contains(t, m) if ((type(m) ~= "number") and (type(m) ~= "string") and (type(m) ~= "boolean")) then return nil, yl_survey.t("member must be primitive") end for _, v in pairs(t) do if (v == m) then return true end end return false end function yl_survey.table_contains(t, m) return table_contains(t, m) end -- Hash name local function hash_name(name) if ((type(name) ~= "string") or (name == "")) then return nil end return minetest.sha256(name, false) end function yl_survey.hash_name(name) return hash_name(name) end -- UUID local function generate_uuid() local template = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx' return string.gsub(template, '[xy]', function(c) local v = (c == 'x') and math.random(0, 15) or math.random(8, 11) return string.format('%x', v) end) end local function is_uuid_duplicate(UUID) for i, record in pairs(yl_survey.data) do local message = "is_uuid_duplicate record.id=%s, UUID=%s" local formatted = string.format(message, dump(record.id), dump(UUID)) log(formatted) if record.id == UUID then return true end end return false end local function create_uuid() local max_attempts = 10 local UUID repeat UUID = generate_uuid() max_attempts = max_attempts - 1 if max_attempts < 0 then return false, "Cannot find non-duplicate UUID" end until (is_uuid_duplicate(UUID) == false) if UUID == "" then return false, "UUID empty" end return true, UUID end function yl_survey.create_uuid() return create_uuid() end -- delete responses local function delete_responses(record) local no = 0 for key, question in pairs(record) do if (tonumber(key) ~= nil) then if question.responses ~= nil then question.responses = nil no = no + 1 end end end return record, no end function yl_survey.delete_responses(record) return delete_responses(record) end -- yl_survey.find_next_free_sort_number(record) local function find_next_free_number(record, target) local highest_no = 1 local duplicates = {} for key, question in pairs(record) do if (tonumber(key) ~= nil) then table.insert(duplicates, question[target]) if (question[target] >= highest_no) then highest_no = question[target] + 1 end end end local is_duplicate = false for _, dupe in ipairs(duplicates) do if (highest_no == dupe) then is_duplicate = true end end return highest_no, is_duplicate end function yl_survey.find_next_free_number(record, target) return find_next_free_number(record, target) end -- yl_survey.is_duplicate(record, sort) local function is_duplicate(record, sort) for key, question in pairs(record) do if (tonumber(key) ~= nil) then if (question[sort] == sort) then return true end end end return false end function yl_survey.is_duplicate(record, sort) return is_duplicate(record, sort) end -- Load and save local function get_savepath() local savepath = yl_survey.worldpath .. yl_survey.settings.save_path log(savepath or "") return savepath end local function get_filepath(filename) local path_to_file = get_savepath() .. DIR_DELIM .. filename .. ".json" log(yl_survey.t("get_filepath", dump(path_to_file))) return path_to_file end local function save_json(filename, content) if ((type(filename) ~= "string") or (type(content) ~= "table")) then return false end local savepath = get_filepath(filename) local savecontent = minetest.write_json(content) return minetest.safe_file_write(savepath, savecontent) end local function load_json(path) local file = io.open(path, "r") if not file then return false, yl_survey.t("error_cannot_open_file", dump(path)) end local content = file:read("*all") file:close() if not content then return false, yl_survey.t("error_cannot_read_file", dump(path)) end return true, minetest.parse_json(content) end -- Public functions wrap the private ones, so they can be exchanged easily function yl_survey.load(filename, ...) return load_json(filename, ...) end function yl_survey.save(filename, content, ...) return save_json(filename, content, ...) end -- Remove file local function remove_file(UUID) local path = get_filepath(UUID) return os.remove(path) end function yl_survey.remove_file(UUID) return remove_file(UUID) end -- Load all data -- local function load_all_data() -- Get all json files from savepath -- Excluding invisible -- Excluding non-json files local save_path = get_savepath() local files = minetest.get_dir_list(save_path, false) or {} local data = {} local total = 0 local good = 0 local bad = 0 for key, filename in ipairs(files) do if is_visible(filename) and is_json(filename) then total = total + 1 local UUID = filename2uuid(filename) local filepath = get_filepath(UUID) local success, content = load_json(filepath) if ((success == true) and (content ~= nil) and (content["metadata"].id == UUID)) then good = good + 1 data[UUID] = content else bad = bad + 1 end end end yl_survey.data = data local message = "[MOD] %s : bad = %s, good = %s, total = %s" local formatted = string.format(message, yl_survey.information.name, tostring(bad), tostring(good), tostring(total)) if bad == 0 then minetest.log("action", formatted) return true, good, bad else minetest.log("warning", formatted) return false, good, bad end end function yl_survey.load_all_data() return load_all_data() end -- Check privs -- local function check_privs(settings) for key, value in pairs(settings) do if ends_with(key, "_privs") then local parts = split(value) for _, part in ipairs(parts) do local message = "[MOD] %s : configured priv %s does not exist." local formatted = string.format(message, yl_survey.information.name, dump(part)) assert(priv_exists(part), formatted) end end end log("PASS priv check") end function yl_survey.check_privs(settings) return check_privs(settings) end