From 28a05c584ce7db892a69aaead0af3e89d5e6db55 Mon Sep 17 00:00:00 2001 From: Nathan Schneider Date: Sat, 1 Jan 2022 23:54:57 -0700 Subject: [PATCH] Minetest checkbox_query now working; checkbox-based change_modules module still needs debugging --- modpol_core/interactions/interactions.lua | 2 +- modpol_core/modules/change_modules.lua | 225 ++++++++------------- modpol_core/modules/display_processes.lua | 2 +- modpol_minetest/overrides/interactions.lua | 97 ++++++++- 4 files changed, 177 insertions(+), 149 deletions(-) diff --git a/modpol_core/interactions/interactions.lua b/modpol_core/interactions/interactions.lua index 96c9055..8bf90de 100644 --- a/modpol_core/interactions/interactions.lua +++ b/modpol_core/interactions/interactions.lua @@ -369,7 +369,7 @@ function modpol.interactions.checkbox_query( return nil end options_display = options_display.. - "List numbers to check (e.g., 1,2,5):" + "List comma-separated options to flip (e.g., 1,2,5):" -- begin displaying print(user .. ": " .. label) print(options_display) diff --git a/modpol_core/modules/change_modules.lua b/modpol_core/modules/change_modules.lua index 58986c7..7878efe 100644 --- a/modpol_core/modules/change_modules.lua +++ b/modpol_core/modules/change_modules.lua @@ -10,7 +10,10 @@ local change_modules = { } change_modules.data = { - result = nil + result = nil, + modules_before = {}, + modules_after = {}, + summary = "", } change_modules.config = { @@ -18,149 +21,95 @@ change_modules.config = { function change_modules:initiate(result) self.data.result = result - -- Step 1: add or remove? - modpol.interactions.dropdown_query( - self.initiator, "Module change options:", - {"Add module","Remove module"}, + self.data.add_modules = {} + self.data.remove_modules = {} + local modules_before = {} + local modules_after = {} + -- generate self.config.modules table + for k, module in pairs(modpol.modules) do + if not modpol.modules[module.slug].hide then + local in_org = false + if self.org.modules[module.slug] then + in_org = true + end + table.insert( + modules_before, + {module.name.." ["..module.slug.."]", in_org}) + end + end + -- send query to user + modpol.interactions.checkbox_query( + self.initiator, + "Check the modules to activate in this org:", + modules_before, function(input) - if input == "Add module" then - self:add_module() - elseif input == "Remove module" then - self:remove_module() - end - end - ) -end - -function change_modules:add_module() - -- prepare module options - local available_modules = modpol.util.copy_table(modpol.modules) - for k,org_mod in pairs(self.org.modules) do - if available_modules[org_mod.slug] then - available_modules[org_mod.slug] = nil - end end - -- present module options - local modules_list = {} - for k,v in pairs(available_modules) do - table.insert(modules_list,v.name) - end - if #modules_list == 0 then - modpol.interactions.message( - self.initiator, "Org has all modules") - modpol.interactions.org_dashboard( - self.initiator, self.org.id) - if self.data.result then self.data.result() end - self.org:delete_process(self.id) - return - end - table.sort(modules_list) - -- now ask which to add - modpol.interactions.dropdown_query( - self.initiator, "Choose a module to add:", - modules_list, - function(mod_choice) - -- confirm choice - modpol.interactions.binary_poll_user( - self.initiator, - "Confirm: propose to add module \"" .. - mod_choice .. "\"?", - function(input) - if input == "Yes" then - self:propose_change("add",mod_choice) - modpol.interactions.org_dashboard( - self.initiator, self.org.id) + -- identify changes + modules_after = input + for i,v in ipairs(modules_after) do + if v[2] ~= modules_before[i][2] then + if v[2] then + table.insert(self.data.add_modules, v[1]) + modpol.msg("add-insert: "..v[1]) else - self:add_module() + table.insert(self.data.remove_modules, v[1]) + modpol.msg("remove-insert: "..v[1]) end end - ) - end - ) -end - -function change_modules:remove_module() - -- prepare module options - local available_modules = {} - for k,org_mod in pairs(self.org.modules) do - if not org_mod.hide then - available_modules[org_mod.slug] = modpol.util.copy_table(org_mod) - end end - local modules_list = {} - local modules_count = 0 - for k,v in pairs(available_modules) do - table.insert(modules_list,v.name) - modules_count = modules_count + 1 - end - -- abort if no modules to remove - if modules_count == 0 then - modpol.interactions.message( - self.initiator, "Org has no modules") - modpol.interactions.org_dashboard( - self.initiator, self.org.id) - if self.data.result then self.data.result() end - self.org:delete_process(self.id) - return - end - table.sort(modules_list) - -- now ask which to remove - modpol.interactions.dropdown_query( - self.initiator, "Choose a module to remove:", - modules_list, - function(mod_choice) - -- confirm choice - modpol.interactions.binary_poll_user( - self.initiator, - "Confirm: propose to remove module \"" .. mod_choice .. "\"?", - function(input) - if input == "Yes" then - self:propose_change("remove",mod_choice) - modpol.interactions.org_dashboard( - self.initiator, self.org.id) - else - self:remove_module() - end - end - ) - end - ) -end - ---- propose_change --- @field type "add" or "remove" -function change_modules:propose_change(type, mod_text) - self.org:call_module( - "consent",self.initiator, - { - prompt = "Do you consent to "..type.. - " this module in org "..self.org.name.. - ":\n"..mod_text, - votes_required = #self.org.members - }, - function() - if type == "add" then - for k,v in pairs(modpol.modules) do - if v.name == mod_text then - table.insert(self.org.modules,v) - end - end - modpol.interactions.message_org( - self.initiator,self.org.id, - "Consent reached:\nAdding \"" - ..mod_text.."\" to org "..self.org.name) - elseif type == "remove" then - local i = 0 - for k,v in pairs(self.org.modules) do - i = i + 1 - if v.name == mod_text then - self.org.modules[k] = nil - end - end - modpol.interactions.message_org( - self.initiator,self.org.id, - "Consent reached:\nRemoving \"" - ..mod_text.."\" from org "..self.org.name) end + -- abort if no changes + if self.data.add_modules == {} + and self.data.remove_modules == {} then + modpol.interactions.message( + self.initiator, "No module changes proposed") + modpol.interactions.org_dashboard( + self.initiator, self.org.id) + self.org:delete_process(self.id) + return + end + -- proceed with consent + local query = "Accept module changes in org ".. + self.org.name.."?" + self.data.summary = "" + if #self.data.add_modules > 0 then + self.data.summary = self.data.summary.."\nAdd: ".. + table.concat(self.data.add_modules,", ") + elseif #self.data.remove_modules > 0 then + summary = "\nRemove: ".. + table.concat(self.data.remove_modules,", ") + end + self.org:call_module( + "consent", + self.initiator, + { + prompt = query..self.data.summary, + votes_required = #self.org.members + }, + function() + self:implement_change() + end) + modpol.interactions.org_dashboard( + self.initiator, self.org.id) end) +end + +function change_modules:implement_change() + for i,v in ipairs(self.data.add_modules) do + local slug = string.match(v,"%[(.+)%]") + self.org.modules[slug] = + modpol.util.copy_table(modpol.modules[slug]) + table.sort(self.org.modules) + end + for i,v in ipairs(self.data.remove_modules) do + local slug = string.match(v,"%[(.+)%]") + self.org.modules[slug] = nil + table.sort(self.org.modules) + end + -- announce and shut down + modpol.interactions.message_org( + self.initiator, + self.org.id, + "Module changes applied to org "..self.org.name..":".. + self.data.summary) if self.data.result then self.data.result() end self.org:delete_process(self.id) end diff --git a/modpol_core/modules/display_processes.lua b/modpol_core/modules/display_processes.lua index 5e4fff2..c851fc5 100644 --- a/modpol_core/modules/display_processes.lua +++ b/modpol_core/modules/display_processes.lua @@ -38,7 +38,7 @@ function display_processes:initiate(result) and type(v2) ~= "table" then v2_string = tostring(v2) elseif type(v2) == "table" then - v2_string = table.concat(v2) + v2_string = tostring(v2) else v2_string = "Could not render" end diff --git a/modpol_minetest/overrides/interactions.lua b/modpol_minetest/overrides/interactions.lua index e39ac6a..b2ef941 100644 --- a/modpol_minetest/overrides/interactions.lua +++ b/modpol_minetest/overrides/interactions.lua @@ -66,13 +66,13 @@ function modpol.interactions.dashboard(user) "size[10,8]", "hypertext[0.5,0.5;9,1;title;Org dashboard]", "label[0.5,2;All orgs:]", - "dropdown[2,1.5;7,0.8;all_orgs;View...,"..formspec_list(all_orgs)..";;]", + "dropdown[2.5,1.5;7,0.8;all_orgs;View...,"..formspec_list(all_orgs)..";;]", "label[0.5,3;Your orgs:]", - "dropdown[2,2.5;7,0.8;user_orgs;View...,"..formspec_list(user_orgs)..";;]", + "dropdown[2.5,2.5;7,0.8;user_orgs;View...,"..formspec_list(user_orgs)..";;]", "label[0.5,4;All users:]", - "dropdown[2,3.5;7,0.8;all_users;View...,"..formspec_list(all_users)..";;]", + "dropdown[2.5,3.5;7,0.8;all_users;View...,"..formspec_list(all_users)..";;]", "label[0.5,5;Pending ("..user_pending_count.."):]", - "dropdown[2,4.5;7,0.8;pending;View...,"..formspec_list(user_pending)..";;]", + "dropdown[2.5,4.5;7,0.8;pending;View...,"..formspec_list(user_pending)..";;]", "button[0.5,7;1,0.8;refresh;Refresh]", "button_exit[8.5,7;1,0.8;close;Close]", } @@ -181,13 +181,13 @@ function modpol.interactions.org_dashboard(user, org_string) minetest.formspec_escape(org.name)..""..membership_toggle(org.name).."]", "label[0.5,1.25;Parent: "..parent..membership_toggle(parent).."]", "label[0.5,2;Members:]", - "dropdown[2,1.5;7,0.8;members;View...,"..formspec_list(members)..";;]", + "dropdown[2.5,1.5;7,0.8;members;View...,"..formspec_list(members)..";;]", "label[0.5,3;Child orgs:]", - "dropdown[2,2.5;7,0.8;children;View...,"..formspec_list(children)..";;]", + "dropdown[2.5,2.5;7,0.8;children;View...,"..formspec_list(children)..";;]", "label[0.5,4;Modules:]", - "dropdown[2,3.5;7,0.8;modules;View...,"..formspec_list(modules)..";;]", + "dropdown[2.5,3.5;7,0.8;modules;View...,"..formspec_list(modules)..";;]", "label[0.5,5;Pending ("..num_pending.."):]", - "dropdown[2,4.5;7,0.8;pending;View...,"..formspec_list(pending)..";;]", + "dropdown[2.5,4.5;7,0.8;pending;View...,"..formspec_list(pending)..";;]", "button[0.5,7;1,0.8;refresh;Refresh]", "button[8.5,7;1,0.8;back;Back]", } @@ -286,7 +286,7 @@ function modpol.interactions.user_dashboard(viewer, user, completion) "size[10,8]", "hypertext[0.5,0.5;9,1;title;User: "..user.."]", "label[0.5,2;User's orgs:]", - "dropdown[2,1.5;7,0.8;user_orgs;View...,"..formspec_list(user_orgs)..";;]", + "dropdown[2.5,1.5;7,0.8;user_orgs;View...,"..formspec_list(user_orgs)..";;]", "button[0.5,7;1.5,0.8;message;Message]", "button_exit[8.5,7;1,0.8;close;Close]", } @@ -484,6 +484,85 @@ minetest.register_on_player_receive_fields(function (player, formname, fields) end end) + +--- Function: modpol.interactions.checkbox_query +-- Allows user to select from a set of options +-- @param user Name of user (string) +-- @param label Query for user before options (string) +-- @param options table of options and their checked status in the form {{"option_1_string", true}, {"option_2_string", false}} +-- @param func function to be called with param "input", made up of the corrected table in the same format as the param options +function modpol.interactions.checkbox_query( + user, label, options, func) + -- set up formspec + -- prepare options + local vertical = 0 + local checkbox_options = {} + for i,v in ipairs(options) do + local fs_line = "" + vertical = i * .5 + fs_line = "checkbox[0,"..vertical..";checkbox_"..i..";".. + v[1]..";"..tostring(v[2]).."]" + table.insert(checkbox_options, fs_line) + end + local max = vertical * 4 + local bar_height = vertical / 2 + local formspec = { + "formspec_version[4]", + "size[10,8]", + "label[0.5,0.5;"..label.."]", + "scrollbaroptions[arrows=default;max="..max..";smallstep=10;largestep=100;thumbsize="..bar_height.."]", + "scrollbar[9,1;0.3,5.5;vertical;scroller;0]", + "scroll_container[0.5,1;9,5.5;scroller;vertical]", + } + -- prepare options + for i,v in ipairs(options) do + local fs_line = "" + local vertical = i * .5 + fs_line = "checkbox[0,"..vertical..";checkbox_"..i..";".. + minetest.formspec_escape(v[1])..";" + ..tostring(v[2]).."]" + table.insert(formspec, fs_line) + end + table.insert(formspec,"scroll_container_end[]") + table.insert(formspec,"button[0.5,7;1.5,0.8;submit;Submit]") + table.insert( + formspec,"button_exit[8,7;1.5,0.8;cancel;Cancel]") + local formspec_string = table.concat(formspec, "") + -- present to players + minetest.show_formspec(user, "modpol:checkbox_query", formspec_string) + -- put func in _contexts + if _contexts[user] == nil then _contexts[user] = {} end + _contexts[user]["checkbox_query_func"] = func + _contexts[user]["checkbox_query_result"] = options +end +-- receive fields +minetest.register_on_player_receive_fields(function (player, formname, fields) + if formname == "modpol:checkbox_query" then + local pname = player:get_player_name() + -- start checking fields + if fields.cancel then + minetest.close_formspec(pname, formname) + elseif fields.submit then + -- send in result + minetest.close_formspec(pname, formname) + _contexts[pname].checkbox_query_func( + _contexts[pname].checkbox_query_result) + else + for k,v in pairs(fields) do + -- identify checkbox actions and flip bool + if string.find(k,"checkbox_") then + local index = tonumber( + string.match(k,"%d+")) + _contexts[pname].checkbox_query_result[index][2] = + not _contexts[pname].checkbox_query_result[index][2] + end + end + end + end +end) + + + -- Function: modpol.binary_poll_user(user, question, function) -- Overrides function at modpol/interactions.lua -- Params: user (string), question (string), func (function)